-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathTypeChecker.swift
3833 lines (3285 loc) · 131 KB
/
TypeChecker.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import Core
import OrderedCollections
import Utils
/// Val's type checker.
public struct TypeChecker {
/// The program being type checked.
public let program: ScopedProgram
/// The diagnostics of the type errors.
private(set) var diagnostics: DiagnosticSet = []
/// A map from translation unit to its imports.
private(set) var imports: [TranslationUnit.ID: Set<ModuleDecl.ID>] = [:]
/// The overarching type of each declaration.
private(set) var declTypes = DeclProperty<AnyType>()
/// The type of each expression.
private(set) var exprTypes = ExprProperty<AnyType>()
/// A map from function and subscript declarations to their implicit captures.
private(set) var implicitCaptures = DeclProperty<[ImplicitCapture]>()
/// A map from generic declarations to their environment.
private(set) var environments = DeclProperty<GenericEnvironment>()
/// A map from module to its synthesized declarations.
private(set) var synthesizedDecls: [ModuleDecl.ID: [SynthesizedDecl]] = [:]
/// A map from name expression to its referred declaration.
private(set) var referredDecls: BindingMap = [:]
/// A map from sequence expressions to their evaluation order.
private(set) var foldedSequenceExprs: [SequenceExpr.ID: FoldedSequenceExpr] = [:]
/// The type relations of the program.
private(set) var relations = TypeRelations()
/// Indicates whether the built-in symbols are visible.
var isBuiltinModuleVisible: Bool
/// The site for which type inference tracing is enabled, if any.
private let inferenceTracingSite: SourceLine?
/// Creates a new type checker for the specified program.
///
/// - Note: `program` is stored in the type checker and mutated throughout type checking (e.g.,
/// to insert synthesized declarations).
public init(
program: ScopedProgram,
isBuiltinModuleVisible: Bool = false,
tracingInferenceIn inferenceTracingSite: SourceLine? = nil
) {
self.program = program
self.isBuiltinModuleVisible = isBuiltinModuleVisible
self.inferenceTracingSite = inferenceTracingSite
}
/// The AST of the program being type checked.
public var ast: AST { program.ast }
/// Reports the given diagnostic.
mutating func report(_ d: Diagnostic) {
diagnostics.insert(d)
}
/// Reports the given diagnostics.
mutating func report<B: Sequence<Diagnostic>>(_ batch: B) {
diagnostics.formUnion(batch)
}
// MARK: Type system
/// Returns a copy of `generic` where occurrences of parameters keying `subtitutions` are
/// replaced by their corresponding value, performing necessary name lookups from `useScope`.
///
/// This method has no effect if `substitutions` is empty.
private mutating func specialized(
_ generic: AnyType, applying substitutions: GenericArguments, in useScope: AnyScopeID
) -> AnyType {
return substitutions.isEmpty
? generic : generic.transform(mutating: &self, specialize(mutating:_:))
func specialize(mutating me: inout Self, _ t: AnyType) -> TypeTransformAction {
switch t.base {
case let u as GenericTypeParameterType:
if let v = substitutions[u.decl] {
return .stepOver((v as? AnyType) ?? .error)
} else {
return .stepOver(t)
}
case let u as AssociatedTypeType:
let d = u.domain.transform(mutating: &me) { (me, t) in specialize(mutating: &me, t) }
let candidates = me.lookup(me.ast[u.decl].baseName, memberOf: d, exposedTo: useScope)
if let c = candidates.uniqueElement {
return .stepOver(MetatypeType(me.realize(decl: c))?.instance ?? .error)
} else {
return .stepOver(.error)
}
case let u as BoundGenericType:
let updatedArguments = u.arguments.mapValues { (v) -> any CompileTimeValue in
if let w = v as? AnyType {
return me.specialized(w, applying: substitutions, in: useScope)
} else {
return v
}
}
return .stepOver(^BoundGenericType(u.base, arguments: updatedArguments))
default:
return .stepInto(t)
}
}
}
/// If `generic` is an unbound generic type, returns a bound generic type mapping its parameters
/// to corresponding value in `substitutions` or a fresh variable if no such value exists.
/// Otherwise, returns `generic` unchanged.
private mutating func bind(_ generic: AnyType, to substitutions: GenericArguments) -> AnyType {
let filtered: GenericArguments?
switch generic.base {
case let u as ProductType:
filtered = extractArguments(of: u.decl, from: substitutions)
case let u as TypeAliasType:
filtered = extractArguments(of: u.decl, from: substitutions)
case let u as MetatypeType:
return ^MetatypeType(of: bind(u.instance, to: substitutions))
default:
return generic
}
return filtered.map({ ^BoundGenericType(generic, arguments: $0) }) ?? generic
}
/// If `d` has generic parameters, returns a table from those parameters to corresponding value
/// in `substitutions`. Otherwise, returns `nil`.
///
/// - Requires: `substitutions` has a value for each generic parameter introduced by `d`.
private mutating func extractArguments<T: GenericDecl>(
of d: T.ID,
from substitutions: GenericArguments
) -> GenericArguments? {
let e = environment(of: d)
if e.parameters.isEmpty { return nil }
return GenericArguments(
uniqueKeysWithValues: e.parameters.map({ (p) in
(key: p, value: substitutions[p]!)
}))
}
/// Returns `s` extended with traits refined by the elements of `s` in `useScope`.
private mutating func derivedTraits(
of s: Set<TraitType>,
in useScope: AnyScopeID
) -> Set<TraitType> {
s.reduce(into: Set<TraitType>()) { (r, t) in
r.formUnion(conformedTraits(of: ^t, in: useScope))
}
}
/// Returns the set of traits to which `type` conforms in `useScope`, visiting all conformance
/// and refinement declarations recursively.
///
/// The returned set only contains traits whose expressions could be evaluated. A diagnostic is
/// reported for all other expressions. `type` is contained in the returned set if it's a trait.
mutating func conformedTraits(of type: AnyType, in useScope: AnyScopeID) -> Set<TraitType> {
var result: Set<TraitType> = []
switch type.base {
case let t as GenericTypeParameterType:
// Generic parameters declared at trait scope conform to that trait.
if let decl = TraitDecl.ID(program[t.decl].scope) {
return conformedTraits(of: ^TraitType(decl, ast: ast), in: useScope)
}
// Conformances of other generic parameters are stored in generic environments.
for s in program.scopes(from: useScope) where useScope.kind.value is GenericScope.Type {
let e = environment(of: s)
result.formUnion(e.conformedTraits(of: type))
}
case let t as BoundGenericType:
result.formUnion(conformedTraits(of: t.base, in: useScope))
case let t as ProductType:
let s = program[t.decl].scope
for u in realize(conformances: ast[t.decl].conformances) {
result.formUnion(conformedTraits(of: ^u, in: s))
}
case let t as TraitType:
result.insert(t)
var work = realize(conformances: ast[t.decl].refinements)
while let base = work.popFirst() {
if base == t {
diagnostics.insert(.error(circularRefinementAt: ast[t.decl].identifier.site))
} else if result.insert(base).inserted {
let newTraits = realize(conformances: ast[base.decl].refinements)
work.formUnion(newTraits)
}
}
// Traits can't be refined in extensions; we're done.
return result
default:
break
}
// Collect traits declared in conformance declarations.
for i in extendingDecls(of: type, exposedTo: useScope).filter(ConformanceDecl.self) {
for t in realize(conformances: ast[i].conformances) {
result.formUnion(conformedTraits(of: ^t, in: useScope))
}
}
return result
}
// MARK: Type checking
/// The status of a type checking request on a declaration.
private enum RequestStatus {
/// Type realization has started.
///
/// The type checker is realizing the overarching type of the declaration. Initiating a new
/// type realization or type checking request on the same declaration will cause a circular
/// dependency error.
case typeRealizationStarted
/// Type realization was completed.
///
/// The checker realized the overarching type of the declaration, which is now available in
/// `declTypes`.
case typeRealizationCompleted
/// Type checking has started.
///
/// The checker is verifying whether the declaration is well-typed; its overarching type is
/// available in `declTypes`. Initiating a new type checking request will cause a circular
/// dependency error.
case typeCheckingStarted
/// Type checking was completed.
///
/// The overarching type is availabe in `declTypes`.
case done
}
/// A cache for type checking requests on declarations.
private var declRequests = DeclProperty<RequestStatus>()
/// The bindings whose initializers are being currently visited.
private var bindingsUnderChecking: DeclSet = []
/// Sets the inferred type of `d` to `type`.
///
/// - Requires: `d` has been assigned to a type yet.
mutating func setInferredType(_ type: AnyType, for d: VarDecl.ID) {
precondition(declTypes[d] == nil)
declTypes[d] = type
}
/// Type checks the specified module, accumulating diagnostics in `self.diagnostics`
///
/// This method is idempotent. After the first call for a module `m`, `self.declTypes[m]` is
/// assigned to an instance of `ModuleType`. Subsequent calls have no effect on `self`.
///
/// - Requires: `m` is a valid ID in the type checker's AST.
public mutating func check(module m: ModuleDecl.ID) {
_check(decl: m) { (this, m) in
this.ast[m].sources.forEach({ this.check(translationUnit: $0) })
}
}
/// Type checks all declarations in `u`.
private mutating func check(translationUnit u: TranslationUnit.ID) {
// The core library is always implicitly imported.
if let m = ast.coreLibrary { imports[u] = [m] }
for d in program[u].decls.lazy.compactMap(ImportDecl.ID.init(_:)) {
registerImport(d, in: u)
}
check(all: ast[u].decls)
}
/// Register the import declared by `d` in translation unit `u`.
private mutating func registerImport(_ d: ImportDecl.ID, in u: TranslationUnit.ID) {
guard let m = ModuleType(realize(importDecl: d))?.decl else { return }
if program.module(containing: u) != m {
imports[u, default: []].insert(m)
} else {
diagnostics.insert(.warning(needlessImport: d, in: ast))
}
}
/// Type checks all declarations in `batch`.
private mutating func check<S: Sequence<AnyDeclID>>(all batch: S) {
batch.forEach({ check(decl: $0) })
}
/// Type checks `d`.
private mutating func check<T: DeclID>(decl d: T) {
switch d.kind {
case AssociatedTypeDecl.self:
break
case AssociatedValueDecl.self:
break
case BindingDecl.self:
check(binding: NodeID(d)!)
case ConformanceDecl.self:
check(conformance: NodeID(d)!)
case ExtensionDecl.self:
check(extension: NodeID(d)!)
case FunctionDecl.self:
check(function: NodeID(d)!)
case GenericParameterDecl.self:
check(genericParameter: NodeID(d)!)
case ImportDecl.self:
check(importDecl: NodeID(d)!)
case InitializerDecl.self:
check(initializer: NodeID(d)!)
case MethodDecl.self:
check(method: NodeID(d)!)
case MethodImpl.self:
check(method: NodeID(program[d].scope)!)
case NamespaceDecl.self:
check(namespace: NodeID(d)!)
case OperatorDecl.self:
check(operator: NodeID(d)!)
case ProductTypeDecl.self:
check(productType: NodeID(d)!)
case SubscriptDecl.self:
check(subscript: NodeID(d)!)
case TraitDecl.self:
check(trait: NodeID(d)!)
case TypeAliasDecl.self:
check(typeAlias: NodeID(d)!)
default:
unexpected(d, in: ast)
}
}
private mutating func check(associatedType d: AssociatedTypeDecl.ID) {
_check(decl: d, { (_, _) in () })
}
private mutating func check(associatedValue d: AssociatedValueDecl.ID) {
_check(decl: d, { (_, _) in () })
}
/// Type checks `d` and returns its type.
///
/// - Note: Method is internal because it may be called during constraint generation.
@discardableResult
mutating func check(binding d: BindingDecl.ID) -> AnyType {
defer { assert(declTypes[d] != nil) }
/// Registers that type checking completed for `d`, assiging it to `t` and returning `t`.
func complete(_ t: AnyType) -> AnyType {
assert(!t[.hasVariable])
declTypes[d] = t
declRequests[d] = .done
return t
}
// Note: binding declarations do not undergo type realization.
switch declRequests[d] {
case nil:
declRequests[d] = .typeCheckingStarted
case .typeCheckingStarted:
diagnostics.insert(.error(circularDependencyAt: ast[d].site))
return complete(.error)
case .done:
return declTypes[d]!
default:
unreachable()
}
// Determine the shape of the declaration.
let shape = inferredType(of: AnyPatternID(ast[d].pattern), shapedBy: nil)
assert(shape.facts.inferredTypes.storage.isEmpty, "expression in binding pattern")
if shape.type[.hasError] {
return complete(.error)
}
// Type check the initializer, if any.
if let initializer = ast[d].initializer {
let initializerType = exprTypes[initializer].setIfNil(^TypeVariable())
var initializerConstraints: [Constraint] = shape.facts.constraints
// The type of the initializer may be a subtype of the pattern's
if program[d].pattern.annotation == nil {
initializerConstraints.append(
EqualityConstraint(
initializerType, shape.type,
origin: ConstraintOrigin(.initializationWithPattern, at: ast[initializer].site)))
} else {
initializerConstraints.append(
SubtypingConstraint(
initializerType, shape.type,
origin: ConstraintOrigin(.initializationWithHint, at: ast[initializer].site)))
}
// Infer the type of the initializer
let names = ast.names(in: ast[d].pattern).map({ AnyDeclID(ast[$0.pattern].decl) })
bindingsUnderChecking.formUnion(names)
let inference = solutionTyping(
initializer,
shapedBy: shape.type,
initialConstraints: initializerConstraints)
bindingsUnderChecking.subtract(names)
// TODO: Complete underspecified generic signatures
// TODO: Ensure that the initializer is either movable or the result of a constructor call
let result = complete(inference.solution.typeAssumptions.reify(shape.type))
// Run deferred queries.
let s = shape.deferred.reduce(true, { $1(&self, inference.solution) && $0 })
assert(s || diagnostics.containsError)
return result
} else {
assert(program[d].pattern.annotation != nil, "expected type annotation")
return complete(shape.type)
}
}
private mutating func check(conformance d: ConformanceDecl.ID) {
_check(decl: d, { (this, d) in this._check(conformance: d) })
}
private mutating func _check(conformance d: ConformanceDecl.ID) {
// Nothing to do if type realization failed.
guard declTypes[d]! != .error else { return }
// Type check the generic constraints.
_ = environment(ofTypeExtendingDecl: d)
check(conformanceList: ast[d].conformances, partOf: d)
check(all: ast[d].members)
}
private mutating func check(extension d: ExtensionDecl.ID) {
_check(decl: d, { (this, d) in this._check(extension: d) })
}
private mutating func _check(extension d: ExtensionDecl.ID) {
// Nothing to do if type realization failed.
guard declTypes[d]! != .error else { return }
// Type check the generic constraints.
_ = environment(ofTypeExtendingDecl: d)
check(all: ast[d].members)
}
/// Type checks the specified function declaration and returns whether that succeeded.
///
/// The type of the declaration must be realizable from type annotations alone or the declaration
/// the declaration must be realized and its inferred type must be stored in `declTyes`. Hence,
/// the method must not be called on the underlying declaration of a lambda or spawn expression
/// before the type of that declaration has been fully inferred.
///
/// - SeeAlso: `checkPending`
private mutating func check(function id: FunctionDecl.ID) {
_check(decl: id, { (this, id) in this._check(function: id) })
}
private mutating func _check(function id: FunctionDecl.ID) {
// Type check the generic constraints.
_ = environment(of: id)
// Type check the parameters.
var parameterNames: Set<String> = []
for parameter in ast[id].parameters {
check(parameter: parameter, siblingNames: ¶meterNames)
}
// Type check the body, if any.
switch ast[id].body {
case .block(let stmt):
check(braceStmt: stmt)
case .expr(let body):
// If `expr` has been used to infer the return type, there's no need to visit it again.
if (ast[id].output == nil) && ast[id].isInExprContext { return }
// Inline functions may return `Never` regardless of their return type.
let r = LambdaType(declTypes[id]!)!.output.skolemized
let (t, c) = typeAndConstraintOfBody(body, inFunctionReturning: r)
_ = solutionTyping(body, shapedBy: t, initialConstraints: [c])
case nil:
// Requirements and FFIs can be without a body.
if program.isRequirement(id) || ast[id].isForeignInterface { return }
// Declaration requires a body.
diagnostics.insert(.error(declarationRequiresBodyAt: ast[id].introducerSite))
}
}
/// Returns `(t, c)` where `t` is the type of the body `e` of a single-expression function whose
/// return type is `r`, and `c` is the constraint placed on `t`.
///
/// Use this method to create initial constraints passed to `solutionTyping` to type check the
/// body of a single-expression function. The returned constraint allows this body to have type
/// `Never` even if the function declares a different return type.
private mutating func typeAndConstraintOfBody(
_ e: AnyExprID, inFunctionReturning r: AnyType
) -> (AnyType, Constraint) {
let t = exprTypes[e].setIfNil(^TypeVariable())
let o = ConstraintOrigin(.return, at: ast[e].site)
let constrainToNever = EqualityConstraint(t, .never, origin: o)
if relations.areEquivalent(r, .never) {
return (t, constrainToNever)
} else {
let c = DisjunctionConstraint(
between: [
.init(constraints: [SubtypingConstraint(t, r, origin: o)], penalties: 0),
.init(constraints: [constrainToNever], penalties: 1),
],
origin: o)
return (t, c)
}
}
private mutating func check(genericParameter d: GenericParameterDecl.ID) {
// TODO: Type check default values.
_check(decl: d, { (_, _) in () })
}
private mutating func check(importDecl d: ImportDecl.ID) {
_check(decl: d, { (_, _) in () })
}
private mutating func check(initializer d: InitializerDecl.ID) {
_check(decl: d, { (this, d) in this._check(initializer: d) })
}
private mutating func _check(initializer d: InitializerDecl.ID) {
// Memberwize initializers trivially type check.
if ast[d].isMemberwise { return }
// Type check the generic constraints.
_ = environment(of: d)
// Type check the parameters.
var parameterNames: Set<String> = []
for parameter in ast[d].parameters {
check(parameter: parameter, siblingNames: ¶meterNames)
}
// Set the type of the implicit receiver declaration.
// Note: the receiver of an initializer is its first parameter.
let type = LambdaType(declTypes[d]!)!
declTypes[ast[d].receiver] = type.inputs[0].type
declRequests[ast[d].receiver] = .typeRealizationCompleted
// Type check the body, if any.
if let body = ast[d].body {
check(braceStmt: body)
} else if !program.isRequirement(d) {
diagnostics.insert(.error(declarationRequiresBodyAt: ast[d].introducer.site))
}
}
private mutating func check(method d: MethodDecl.ID) {
_check(decl: d, { (this, d) in this._check(method: d) })
}
private mutating func _check(method d: MethodDecl.ID) {
// Type check the generic constraints.
_ = environment(of: d)
// Type check the parameters.
var parameterNames: Set<String> = []
for parameter in ast[d].parameters {
check(parameter: parameter, siblingNames: ¶meterNames)
}
// Type check the bodies.
let bundle = MethodType(declTypes[d])!
for v in ast[d].impls {
declTypes[ast[v].receiver] = ^ParameterType(ast[v].introducer.value, bundle.receiver)
declRequests[ast[v].receiver] = .done
check(methodImpl: v)
}
}
private mutating func check(methodImpl d: MethodImpl.ID) {
switch ast[d].body {
case .expr(let e):
_ = checkedType(of: e, subtypeOf: LambdaType(declTypes[d])!.output)
case .block(let s):
check(braceStmt: s)
case nil:
if !program.isRequirement(d) {
diagnostics.insert(.error(declarationRequiresBodyAt: ast[d].introducer.site))
}
}
declRequests[d] = .done
}
/// Inserts in `siblingNames` the name of the parameter declaration identified by `d`.
private mutating func check(parameter d: ParameterDecl.ID, siblingNames: inout Set<String>) {
// Check for duplicate parameter names.
if !siblingNames.insert(ast[d].baseName).inserted {
diagnostics.insert(.error(duplicateParameterNamed: ast[d].baseName, at: ast[d].site))
}
// Type check the default value, if any.
if let defaultValue = ast[d].defaultValue {
let parameterType = ParameterType(declTypes[d]!)!
let defaultValueType = exprTypes[defaultValue].setIfNil(^TypeVariable())
_ = solutionTyping(
defaultValue, shapedBy: parameterType.bareType,
initialConstraints: [
ParameterConstraint(
defaultValueType, ^parameterType,
origin: ConstraintOrigin(.argument, at: ast[d].site))
])
}
declRequests[d] = .typeRealizationCompleted
}
private mutating func check(namespace d: NamespaceDecl.ID) {
_check(decl: d, { (this, d) in this._check(namespace: d) })
}
private mutating func _check(namespace d: NamespaceDecl.ID) {
for m in ast[d].members {
check(decl: m)
}
}
private mutating func check(operator d: OperatorDecl.ID) {
// Look for duplicate operator declaration.
let source = TranslationUnit.ID(program[d].scope)!
for decl in ast[source].decls where decl.kind == OperatorDecl.self {
let oper = OperatorDecl.ID(decl)!
if oper != d,
ast[oper].notation.value == ast[d].notation.value,
ast[oper].name.value == ast[d].name.value
{
diagnostics.insert(.error(duplicateOperatorNamed: ast[d].name.value, at: ast[d].site))
}
}
}
private mutating func check(productType d: ProductTypeDecl.ID) {
_check(decl: d, { (this, d) in this._check(productType: d) })
}
private mutating func _check(productType d: ProductTypeDecl.ID) {
_ = environment(of: d)
check(all: ast[d].members)
check(conformanceList: ast[d].conformances, partOf: d)
}
private mutating func check(subscript d: SubscriptDecl.ID) {
_check(decl: d, { (this, d) in this._check(subscript: d) })
}
private mutating func _check(subscript d: SubscriptDecl.ID) {
// The type of the declaration must have been realized.
let declType = SubscriptType(declTypes[d]!)!
let outputType = declType.output.skolemized
// Type check the generic constraints.
_ = environment(of: d)
// Type check the parameters, if any.
if let parameters = ast[d].parameters {
var parameterNames: Set<String> = []
for parameter in parameters {
check(parameter: parameter, siblingNames: ¶meterNames)
}
}
// Type checks the subscript's implementations.
for v in ast[d].impls {
if let receiver = ast[v].receiver {
declTypes[receiver] = ^ParameterType(RemoteType(declType.captures.first!.type)!)
declRequests[receiver] = .typeRealizationCompleted
}
check(subscriptImpl: v, outputType: outputType)
}
}
private mutating func check(subscriptImpl d: SubscriptImpl.ID, outputType: AnyType) {
switch ast[d].body {
case .expr(let e):
_ = checkedType(of: e, subtypeOf: outputType)
case .block(let s):
check(braceStmt: s)
case nil:
if !program.isRequirement(d) {
diagnostics.insert(.error(declarationRequiresBodyAt: ast[d].introducer.site))
}
}
declRequests[d] = .done
}
private mutating func check(trait d: TraitDecl.ID) {
_check(decl: d, { (this, d) in this._check(trait: d) })
}
private mutating func _check(trait d: TraitDecl.ID) {
let t = declTypes[d]!
guard !t.isError else { return }
_ = environment(ofTrait: d)
check(all: ast[d].members)
check(all: extendingDecls(of: t, exposedTo: program[d].scope))
// TODO: Check refinements
}
private mutating func check(typeAlias d: TypeAliasDecl.ID) {
_check(decl: d, { (this, id) in this._check(typeAlias: id) })
}
private mutating func _check(typeAlias d: TypeAliasDecl.ID) {
guard let t = MetatypeType(declTypes[d]!)?.instance else { return }
_ = environment(of: d)
check(all: extendingDecls(of: t, exposedTo: program[d].scope))
// TODO: Check conformances
}
/// Type checks `d` using `check` iff `d` hasn't been checked already.
///
/// - Postcondition: `declRequests[d]` is `.typeCheckingCompleted`.
private mutating func _check<T: DeclID>(decl d: T, _ check: (inout Self, T) -> Void) {
if prepareForTypeChecking(d) != .done {
check(&self, d)
declRequests[d] = .done
}
}
/// Ensures that the overarching type of `d` has been realized, returning its request status.
private mutating func prepareForTypeChecking<T: DeclID>(_ d: T) -> RequestStatus {
switch declRequests[d] {
case nil:
// Realize the type of the declaration before starting type checking.
if realize(decl: d).isError {
// Type checking fails if type realization did.
declRequests[d] = .done
return .done
} else {
// Note: Because the type realization of certain declarations may escalate to type
// checking perform type checking, we should re-check the status of the request.
return prepareForTypeChecking(d)
}
case .typeRealizationCompleted:
declRequests[d] = .typeCheckingStarted
return .typeCheckingStarted
case .typeRealizationStarted, .typeCheckingStarted:
diagnostics.insert(.error(circularDependencyAt: ast[d].site))
declRequests[d] = .done
return .done
case .done:
return .done
}
}
/// Type check the conformance list `traits` that's part of declaration `d`.
private mutating func check<T: Decl & LexicalScope>(
conformanceList traits: [NameExpr.ID], partOf d: T.ID
) {
let receiver = realizeReceiver(in: d)!.instance
for e in traits {
guard let rhs = realize(name: e)?.instance else {
continue
}
guard rhs.base is TraitType else {
diagnostics.insert(.error(conformanceToNonTraitType: rhs, at: ast[e].site))
continue
}
for t in conformedTraits(of: rhs, in: program[d].scope) {
checkAndRegisterConformance(of: receiver, to: t, declaredBy: d, at: ast[e].site)
}
}
}
/// Registers the conformance of `model` to `trait` declared by `source` in `self.relations` if
/// it is satisfied. Otherwise, reports diagnostics at `declSite`.
private mutating func checkAndRegisterConformance<T: Decl & LexicalScope>(
of model: AnyType,
to trait: TraitType,
declaredBy source: T.ID,
at declSite: SourceRange
) {
guard let c = checkConformance(of: model, to: trait, declaredBy: source, at: declSite)
else {
// Diagnostics have been reported by `checkConformance`.
return
}
let (inserted, x) = relations.insert(c, testingContainmentWith: program)
if !inserted {
diagnostics.insert(.error(redundantConformance: c, at: declSite, alreadyDeclaredAt: x.site))
}
}
/// Returns the conformance of `model` to `trait` declared by `source` if it's satisfied.
/// Otherwise, reports missing requirements at `declSite` and returns `nil`.
private mutating func checkConformance<T: Decl & LexicalScope>(
of model: AnyType,
to trait: TraitType,
declaredBy source: T.ID,
at declSite: SourceRange
) -> Conformance? {
let useScope = AnyScopeID(source)
let specializations: GenericArguments = [ast[trait.decl].selfParameterDecl: model]
// Check the trait's requirements.
var implementations = Conformance.ImplementationMap()
var notes: DiagnosticSet = []
for m in ast[trait.decl].members {
checkStatisifed(requirement: m)
}
if !notes.isEmpty {
diagnostics.insert(.error(model, doesNotConformTo: trait, at: declSite, because: notes))
return nil
}
// Conformances at file scope are exposed in the whole module. Other conformances are exposed
// in their containing scope.
let expositionScope = read(program[source].scope) { (s) in
(s.kind == TranslationUnit.self) ? AnyScopeID(program.module(containing: s)) : s
}
// FIXME: Use bound generic parameters as conditions
let m = BoundGenericType(model).map(\.base) ?? model
return Conformance(
model: m, concept: trait, arguments: [:], conditions: [],
source: AnyDeclID(source), scope: expositionScope,
implementations: implementations,
site: declSite)
/// Checks if requirement `d` is satisfied by `model`, extending `implementations` if it is or
/// reporting a diagnostic in `notes` otherwise.
func checkStatisifed(requirement d: AnyDeclID) {
switch d.kind {
case GenericParameterDecl.self:
assert(d == ast[trait.decl].selfParameterDecl, "unexpected declaration")
case AssociatedTypeDecl.self:
// TODO: Implement me.
break
case AssociatedValueDecl.self:
// TODO: Implement me.
break
case FunctionDecl.self:
checkSatisfied(function: .init(d)!)
case InitializerDecl.self:
checkSatisfied(initializer: .init(d)!)
case MethodDecl.self:
let r = MethodDecl.ID(d)!
let n = Name(of: r, in: ast)
ast[r].impls.forEach({ checkSatisfied(variant: $0, inMethod: n) })
case SubscriptDecl.self:
// TODO: Implement me.
break
default:
unreachable()
}
}
/// Checks if requirement `d` is satisfied by `model`, extending `implementations` if it is or
/// reporting a diagnostic in `notes` otherwise.
func checkSatisfied(initializer d: InitializerDecl.ID) {
let requiredType = relations.canonical(
specialized(realize(decl: d), applying: specializations, in: useScope))
guard !requiredType[.hasError] else { return }
if let c = implementation(
of: Name(of: d, in: ast), in: model,
withCallableType: LambdaType(requiredType)!, specializedWith: specializations,
exposedTo: useScope)
{
implementations[d] = .concrete(c)
} else {
notes.insert(.note(trait: trait, requiresInitializer: requiredType, at: declSite))
}
}
/// Checks if requirement `d` is satisfied by `model`, extending `implementations` if it is or
/// reporting a diagnostic in `notes` otherwise.
func checkSatisfied(function d: FunctionDecl.ID) {
let requiredType = specialized(realize(decl: d), applying: specializations, in: useScope)
guard !requiredType[.hasError] else { return }
let t = relations.canonical(requiredType)
let requiredName = Name(of: d, in: ast)!
if let c = implementation(
of: requiredName, in: model,
withCallableType: LambdaType(t)!, specializedWith: specializations,
exposedTo: useScope)
{
implementations[d] = .concrete(c)
} else if let i = synthesizedImplementation(of: d, for: t, in: useScope) {
implementations[d] = .synthetic(i)
synthesizedDecls[program.module(containing: source), default: []].append(i)
} else {
notes.insert(
.note(trait: trait, requiresMethod: requiredName, withType: requiredType, at: declSite))
}
}
/// Checks if requirement `d` of a method bunde named `m` is satisfied by `model`, extending
/// `implementations` if it is or reporting a diagnostic in `notes` otherwise.
func checkSatisfied(variant d: MethodImpl.ID, inMethod m: Name) {
let requiredType = specialized(realize(decl: d), applying: specializations, in: useScope)
guard !requiredType[.hasError] else { return }
let t = relations.canonical(requiredType)
if let c = implementation(
of: m, in: model,
withCallableType: LambdaType(t)!, specializedWith: specializations,
exposedTo: useScope)
{
implementations[d] = .concrete(c)
} else if let i = synthesizedImplementation(of: d, for: t, in: useScope) {
implementations[d] = .synthetic(i)
synthesizedDecls[program.module(containing: d), default: []].append(i)
} else {
let requiredName = m.appending(ast[d].introducer.value)!
notes.insert(
.note(trait: trait, requiresMethod: requiredName, withType: requiredType, at: declSite))
}
}
}
/// Returns the declaration exposed to `scope` of a callable member in `model` that introduces
/// `requirementName` with type `requiredType`, using `specializations` to subsititute
/// associated types and values. Returns `nil` if zero or more than 1 candidates were found.
private mutating func implementation(
of requirementName: Name,
in model: AnyType,
withCallableType requiredType: LambdaType,
specializedWith specializations: GenericArguments,
exposedTo scope: AnyScopeID
) -> AnyDeclID? {
/// Returns `true` if candidate `d` has `requirementType`.
func hasRequiredType<T: Decl>(_ d: T.ID) -> Bool {
relations.areEquivalent(
specialized(realize(decl: d), applying: specializations, in: scope),
^requiredType)
}
let allCandidates = lookup(requirementName.stem, memberOf: model, exposedTo: scope)
let viableCandidates = allCandidates.compactMap { (c) -> AnyDeclID? in
// TODO: Filter out the candidates with incompatible constraints.
// trait A {}
// type Foo<T> {}
// extension Foo where T: U { fun foo() }
// conformance Foo: A {} // <- should not consider `foo` in the extension
switch c.kind {
case FunctionDecl.self:
let d = FunctionDecl.ID(c)!
return ((ast[d].body != nil) && hasRequiredType(d)) ? c : nil
case InitializerDecl.self:
let d = InitializerDecl.ID(c)!
return ((ast[d].body != nil) && hasRequiredType(d)) ? c : nil
case MethodDecl.self:
for d in ast[MethodDecl.ID(c)!].impls where ast[d].body != nil {