Skip to content

Commit 4b7589d

Browse files
authored
Merge pull request #9916 from slavapestov/lazier-type-checking-of-lazy-vars
Lazier type checking of lazy vars
2 parents 0e91d05 + d8234ba commit 4b7589d

16 files changed

+143
-94
lines changed

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7581,12 +7581,11 @@ Expr *ConstraintSystem::applySolutionShallow(const Solution &solution,
75817581
Expr *Solution::coerceToType(Expr *expr, Type toType,
75827582
ConstraintLocator *locator,
75837583
bool ignoreTopLevelInjection,
7584-
bool skipClosures,
75857584
Optional<Pattern*> typeFromPattern) const {
75867585
auto &cs = getConstraintSystem();
75877586
ExprRewriter rewriter(cs, *this,
75887587
/*suppressDiagnostics=*/false,
7589-
/*skipClosures=*/skipClosures);
7588+
/*skipClosures=*/false);
75907589
Expr *result = rewriter.coerceToType(expr, toType, locator, typeFromPattern);
75917590
if (!result)
75927591
return nullptr;

lib/Sema/ConstraintSystem.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -575,17 +575,13 @@ class Solution {
575575
/// on a suspicious top-level optional injection (because the caller already
576576
/// diagnosed it).
577577
///
578-
/// \param skipClosures Whether to skip bodies of non-single expression
579-
/// closures.
580-
///
581578
/// \param typeFromPattern Optionally, the caller can specify the pattern
582579
/// from where the toType is derived, so that we can deliver better fixit.
583580
///
584581
/// \returns the coerced expression, which will have type \c ToType.
585582
Expr *coerceToType(Expr *expr, Type toType,
586583
ConstraintLocator *locator,
587584
bool ignoreTopLevelInjection = false,
588-
bool skipClosures = false,
589585
Optional<Pattern*> typeFromPattern = None) const;
590586

591587
/// \brief Convert the given expression to a logic value.

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 60 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,10 @@ bool ExprTypeCheckListener::builtConstraints(ConstraintSystem &cs, Expr *expr) {
16311631
return false;
16321632
}
16331633

1634+
Expr *ExprTypeCheckListener::foundSolution(Solution &solution, Expr *expr) {
1635+
return expr;
1636+
}
1637+
16341638
Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) {
16351639
return expr;
16361640
}
@@ -1864,14 +1868,24 @@ bool TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
18641868
expr->setType(ErrorType::get(Context));
18651869
return false;
18661870
}
1867-
1868-
// Apply the solution to the expression.
1871+
1872+
auto result = expr;
18691873
auto &solution = viable[0];
1874+
if (listener) {
1875+
result = listener->foundSolution(solution, result);
1876+
if (!result)
1877+
return true;
1878+
}
1879+
1880+
if (options.contains(TypeCheckExprFlags::SkipApplyingSolution))
1881+
return false;
1882+
1883+
// Apply the solution to the expression.
18701884
bool isDiscarded = options.contains(TypeCheckExprFlags::IsDiscarded);
18711885
bool skipClosures = options.contains(TypeCheckExprFlags::SkipMultiStmtClosures);
1872-
auto result = cs.applySolution(solution, expr, convertType.getType(),
1873-
isDiscarded, suppressDiagnostics,
1874-
skipClosures);
1886+
result = cs.applySolution(solution, result, convertType.getType(),
1887+
isDiscarded, suppressDiagnostics,
1888+
skipClosures);
18751889
if (!result) {
18761890
// Failure already diagnosed, above, as part of applying the solution.
18771891
return true;
@@ -2111,26 +2125,25 @@ bool TypeChecker::typeCheckExpressionShallow(Expr *&expr, DeclContext *dc) {
21112125
}
21122126

21132127
bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
2114-
DeclContext *DC, bool skipClosures) {
2128+
DeclContext *DC, bool skipApplyingSolution) {
21152129

21162130
/// Type checking listener for pattern binding initializers.
21172131
class BindingListener : public ExprTypeCheckListener {
21182132
Pattern *&pattern;
21192133
Expr *&initializer;
2120-
DeclContext *DC;
2121-
bool skipClosures;
21222134

21232135
/// The locator we're using.
21242136
ConstraintLocator *Locator;
21252137

21262138
/// The type of the initializer.
21272139
Type InitType;
2128-
2140+
21292141
public:
2130-
explicit BindingListener(Pattern *&pattern, Expr *&initializer,
2131-
DeclContext *DC, bool skipClosures)
2132-
: pattern(pattern), initializer(initializer), DC(DC),
2133-
skipClosures(skipClosures) { }
2142+
explicit BindingListener(Pattern *&pattern, Expr *&initializer)
2143+
: pattern(pattern), initializer(initializer),
2144+
Locator(nullptr) { }
2145+
2146+
Type getInitType() const { return InitType; }
21342147

21352148
bool builtConstraints(ConstraintSystem &cs, Expr *expr) override {
21362149
// Save the locator we're using for the expression.
@@ -2150,44 +2163,32 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
21502163
return false;
21512164
}
21522165

2153-
Expr *appliedSolution(Solution &solution, Expr *expr) override {
2166+
Expr *foundSolution(Solution &solution, Expr *expr) override {
21542167
// Figure out what type the constraints decided on.
2155-
auto &cs = solution.getConstraintSystem();
2156-
auto &tc = cs.getTypeChecker();
21572168
InitType = solution.simplifyType(InitType);
21582169

2170+
// Just keep going.
2171+
return expr;
2172+
}
2173+
2174+
Expr *appliedSolution(Solution &solution, Expr *expr) override {
21592175
// Convert the initializer to the type of the pattern.
21602176
// ignoreTopLevelInjection = Binding->isConditional()
21612177
expr = solution.coerceToType(expr, InitType, Locator,
2162-
false /* ignoreTopLevelInjection */,
2163-
skipClosures);
2178+
false /* ignoreTopLevelInjection */);
21642179
if (!expr) {
21652180
return nullptr;
21662181
}
21672182

2168-
// Force the initializer to be materializable.
2169-
// FIXME: work this into the constraint system
2170-
expr = tc.coerceToMaterializable(expr);
2183+
assert(expr->getType()->isEqual(InitType));
21712184

2172-
// Apply the solution to the pattern as well.
2173-
Type patternType = expr->getType();
2174-
2175-
TypeResolutionOptions options;
2176-
options |= TR_OverrideType;
2177-
options |= TR_InExpression;
2178-
if (isa<EditorPlaceholderExpr>(expr->getSemanticsProvidingExpr())) {
2179-
options |= TR_EditorPlaceholder;
2180-
}
2181-
if (tc.coercePatternToType(pattern, DC, patternType, options)) {
2182-
return nullptr;
2183-
}
21842185
initializer = expr;
21852186
return expr;
21862187
}
21872188
};
21882189

21892190
assert(initializer && "type-checking an uninitialized binding?");
2190-
BindingListener listener(pattern, initializer, DC, skipClosures);
2191+
BindingListener listener(pattern, initializer);
21912192

21922193
TypeLoc contextualType;
21932194
auto contextualPurpose = CTP_Unused;
@@ -2209,18 +2210,34 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
22092210

22102211
// Type-check the initializer.
22112212
TypeCheckExprOptions flags = TypeCheckExprFlags::ConvertTypeIsOnlyAHint;
2212-
if (skipClosures)
2213-
flags |= TypeCheckExprFlags::SkipMultiStmtClosures;
2213+
if (skipApplyingSolution)
2214+
flags |= TypeCheckExprFlags::SkipApplyingSolution;
22142215

22152216
bool hadError = typeCheckExpression(initializer, DC, contextualType,
22162217
contextualPurpose,
22172218
flags,
22182219
&listener);
2219-
2220-
if (hadError && !initializer->getType()) {
2221-
initializer->setType(ErrorType::get(Context));
2220+
2221+
if (!hadError) {
2222+
TypeResolutionOptions options;
2223+
options |= TR_OverrideType;
2224+
options |= TR_InExpression;
2225+
if (isa<EditorPlaceholderExpr>(initializer->getSemanticsProvidingExpr())) {
2226+
options |= TR_EditorPlaceholder;
2227+
}
2228+
2229+
// Apply the solution to the pattern as well.
2230+
if (coercePatternToType(pattern, DC, listener.getInitType(), options)) {
2231+
return true;
2232+
}
22222233
}
22232234

2235+
if (hadError && !initializer->getType())
2236+
initializer->setType(ErrorType::get(Context));
2237+
2238+
// If the type of the pattern is inferred, assign error types to the pattern
2239+
// and its variables, to prevent it from being referenced by the constraint
2240+
// system.
22242241
if (hadError &&
22252242
(!pattern->hasType() ||
22262243
pattern->getType()->hasUnboundGenericType())) {
@@ -2242,7 +2259,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
22422259

22432260
bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
22442261
unsigned patternNumber,
2245-
bool skipClosures) {
2262+
bool skipApplyingSolution) {
22462263

22472264
Pattern *pattern = PBD->getPattern(patternNumber);
22482265
Expr *init = PBD->getInit(patternNumber);
@@ -2262,11 +2279,10 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
22622279
DC = initContext;
22632280
}
22642281

2265-
bool hadError = typeCheckBinding(pattern, init, DC, skipClosures);
2282+
bool hadError = typeCheckBinding(pattern, init, DC, skipApplyingSolution);
22662283
PBD->setPattern(patternNumber, pattern, initContext);
22672284
PBD->setInit(patternNumber, init);
22682285

2269-
22702286
// If we entered an initializer context, contextualize any
22712287
// auto-closures we might have created.
22722288
if (initContext) {
@@ -2600,7 +2616,8 @@ bool TypeChecker::typeCheckStmtCondition(StmtCondition &cond, DeclContext *dc,
26002616
// If the pattern didn't get a type, it's because we ran into some
26012617
// unknown types along the way. We'll need to check the initializer.
26022618
auto init = elt.getInitializer();
2603-
hadError |= typeCheckBinding(pattern, init, dc, /*skipClosures*/false);
2619+
hadError |= typeCheckBinding(pattern, init, dc,
2620+
/*skipApplyingSolution*/false);
26042621
elt.setPattern(pattern);
26052622
elt.setInitializer(init);
26062623
hadAnyFalsable |= pattern->isRefutablePattern();
@@ -2917,7 +2934,6 @@ bool TypeChecker::convertToType(Expr *&expr, Type type, DeclContext *dc,
29172934
Expr *result = solution.coerceToType(expr, type,
29182935
cs.getConstraintLocator(expr),
29192936
/*ignoreTopLevelInjection*/false,
2920-
/*skipClosures*/false,
29212937
typeFromPattern);
29222938
if (!result) {
29232939
return true;

lib/Sema/TypeCheckDecl.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,11 +1198,11 @@ static void validatePatternBindingEntry(TypeChecker &tc,
11981198
// If the pattern didn't get a type or if it contains an unbound generic type,
11991199
// we'll need to check the initializer.
12001200
if (!pattern->hasType() || pattern->getType()->hasUnboundGenericType()) {
1201-
bool skipClosures = false;
1201+
bool skipApplyingSolution = false;
12021202
if (auto var = binding->getSingleVar())
1203-
skipClosures = var->getAttrs().hasAttribute<LazyAttr>();
1203+
skipApplyingSolution = var->getAttrs().hasAttribute<LazyAttr>();
12041204

1205-
if (tc.typeCheckPatternBinding(binding, entryNumber, skipClosures))
1205+
if (tc.typeCheckPatternBinding(binding, entryNumber, skipApplyingSolution))
12061206
return;
12071207
}
12081208

@@ -4047,7 +4047,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
40474047
if (!IsFirstPass) {
40484048
for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) {
40494049
if (!PBD->isInitializerChecked(i) && PBD->getInit(i))
4050-
TC.typeCheckPatternBinding(PBD, i, /*skipClosures*/false);
4050+
TC.typeCheckPatternBinding(PBD, i, /*skipApplyingSolution*/false);
40514051
}
40524052
}
40534053

@@ -4078,7 +4078,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
40784078
// If we got a default initializer, install it and re-type-check it
40794079
// to make sure it is properly coerced to the pattern type.
40804080
PBD->setInit(i, defaultInit);
4081-
TC.typeCheckPatternBinding(PBD, i, /*skipClosures*/false);
4081+
TC.typeCheckPatternBinding(PBD, i, /*skipApplyingSolution*/false);
40824082
}
40834083
}
40844084
}

lib/Sema/TypeChecker.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ enum class TypeCheckExprFlags {
224224
/// Set if the client prefers fixits to be in the form of force unwrapping
225225
/// or optional chaining to return an optional.
226226
PreferForceUnwrapToOptional = 0x80,
227+
228+
/// If set, don't apply a solution.
229+
SkipApplyingSolution = 0x100,
227230
};
228231

229232
typedef OptionSet<TypeCheckExprFlags> TypeCheckExprOptions;
@@ -325,6 +328,13 @@ class ExprTypeCheckListener {
325328
/// constraint system, or false otherwise.
326329
virtual bool builtConstraints(constraints::ConstraintSystem &cs, Expr *expr);
327330

331+
/// Callback invoked once a solution has been found.
332+
///
333+
/// The callback may further alter the expression, returning either a
334+
/// new expression (to replace the result) or a null pointer to indicate
335+
/// failure.
336+
virtual Expr *foundSolution(constraints::Solution &solution, Expr *expr);
337+
328338
/// Callback invokes once the chosen solution has been applied to the
329339
/// expression.
330340
///
@@ -1667,9 +1677,9 @@ class TypeChecker final : public LazyResolver {
16671677

16681678
/// Type-check an initialized variable pattern declaration.
16691679
bool typeCheckBinding(Pattern *&P, Expr *&Init, DeclContext *DC,
1670-
bool skipClosures);
1680+
bool skipApplyingSolution);
16711681
bool typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned patternNumber,
1672-
bool skipClosures);
1682+
bool skipApplyingSolution);
16731683

16741684
/// Type-check a for-each loop's pattern binding and sequence together.
16751685
bool typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt);

test/Constraints/array_literal.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,15 @@ func defaultToAny(i: Int, s: String) {
129129

130130
let a2: Array = [1, "a", 3.5]
131131
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}}
132-
let _: Int = a2 // expected-error{{value of type '[Any]'}}
132+
let _: Int = a2 // expected-error{{value of type 'Array<Any>'}}
133133

134134
let a3 = [1, "a", nil, 3.5]
135135
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}}
136136
let _: Int = a3 // expected-error{{value of type '[Any?]'}}
137137

138138
let a4: Array = [1, "a", nil, 3.5]
139139
// expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}}
140-
let _: Int = a4 // expected-error{{value of type '[Any?]'}}
140+
let _: Int = a4 // expected-error{{value of type 'Array<Any?>'}}
141141

142142
let a5 = []
143143
// expected-error@-1{{empty collection literal requires an explicit type}}

test/IDE/print_types.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,14 @@ func testVariableTypes(_ param: Int, param2: inout Double) {
5757
_ = typealias1 ; typealias1 = 1
5858

5959
var optional1 = Optional<Int>.none
60-
// CHECK: VarDecl '''optional1''' Optional<Int>{{$}}
61-
// FULL: VarDecl '''optional1''' Swift.Optional<Swift.Int>{{$}}
60+
// CHECK: VarDecl '''optional1''' Int?{{$}}
61+
// FULL: VarDecl '''optional1''' Swift.Int?{{$}}
6262
_ = optional1 ; optional1 = nil
6363

6464
var optional2 = Optional<[Int]>.none
6565
_ = optional2 ; optional2 = nil
66-
// CHECK: VarDecl '''optional2''' Optional<[Int]>{{$}}
67-
// FULL: VarDecl '''optional2''' Swift.Optional<[Swift.Int]>{{$}}
66+
// CHECK: VarDecl '''optional2''' [Int]?{{$}}
67+
// FULL: VarDecl '''optional2''' [Swift.Int]?{{$}}
6868
}
6969

7070
func testFuncType1() {}

test/Interpreter/repl.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ for c in "foobar".unicodeScalars { print(c) }
148148
// CHECK-NEXT: r
149149

150150
var vec = Array<String>()
151-
// CHECK: vec : Array<String> = []
151+
// CHECK: vec : [String] = []
152152

153153
// Error recovery
154154
var a : [int]

0 commit comments

Comments
 (0)