diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 8e4fe561a2f4f..d5f367f967b0e 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1351,6 +1351,13 @@ namespace { Type visitDeclRefExpr(DeclRefExpr *E) { auto locator = CS.getConstraintLocator(E); + auto invalidateReference = [&]() -> Type { + auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole); + (void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator)); + CS.setType(E, hole); + return hole; + }; + Type knownType; if (auto *VD = dyn_cast(E->getDecl())) { knownType = CS.getTypeIfAvailable(VD); @@ -1358,13 +1365,27 @@ namespace { knownType = CS.getVarType(VD); if (knownType) { + // An out-of-scope type variable(s) could appear the type of + // a declaration only in diagnostic mode when invalid variable + // declaration is recursively referenced inside of a multi-statement + // closure located somewhere within its initializer e.g.: + // `let x = [] { ... print(x) }`. It happens because the + // variable assumes the result type of its initializer unless + // its specified explicitly. + if (isa(CurDC) && knownType->hasTypeVariable()) { + if (knownType.findIf([&](Type type) { + auto *typeVar = type->getAs(); + if (!typeVar || CS.getFixedType(typeVar)) + return false; + + return !CS.isActiveTypeVariable(typeVar); + })) + return invalidateReference(); + } + // If the known type has an error, bail out. if (knownType->hasError()) { - auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole); - (void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator)); - if (!CS.hasType(E)) - CS.setType(E, hole); - return hole; + return invalidateReference(); } if (!knownType->hasPlaceholder()) { @@ -1381,10 +1402,7 @@ namespace { // (in getTypeOfReference) so we can match non-error param types. if (!knownType && E->getDecl()->isInvalid() && !CS.isForCodeCompletion()) { - auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole); - (void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator)); - CS.setType(E, hole); - return hole; + return invalidateReference(); } // Create an overload choice referencing this declaration and immediately diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index a44ffd992c424..beff805b7dc84 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -74,7 +74,7 @@ class TypeVariableRefFinder : public ASTWalker { if (auto *DRE = dyn_cast(expr)) { auto *decl = DRE->getDecl(); - if (auto type = CS.getTypeIfAvailable(DRE->getDecl())) { + if (auto type = CS.getTypeIfAvailable(decl)) { auto &ctx = CS.getASTContext(); // If this is not one of the closure parameters which // is inferrable from the body, let's replace type diff --git a/test/expr/closure/multi_statement.swift b/test/expr/closure/multi_statement.swift index ea80638084f16..38c35ce3afdec 100644 --- a/test/expr/closure/multi_statement.swift +++ b/test/expr/closure/multi_statement.swift @@ -650,3 +650,46 @@ func test_that_closures_are_attempted_in_order() { return false } } + +func test_use_of_concrete_params_in_for_condition() { + struct S { + var cond: Bool + } + + func test(_: (S) -> Void) {} + + test { data in + for i in 0...10 where !data.cond { // Ok + print(i) + } + } +} + +// https://github.com/apple/swift/issues/63455 +func test_recursive_var_reference_in_multistatement_closure() { + struct MyStruct { + func someMethod() {} + } + + func takeClosure(_ x: () -> Void) {} + + func test(optionalInt: Int?, themes: MyStruct?) { + takeClosure { + let int = optionalInt { // expected-error {{cannot call value of non-function type 'Int?'}} + print(int) + } + } + + takeClosure { + let theme = themes?.someMethod() { // expected-error {{extra trailing closure passed in call}} + _ = theme + } + } + + takeClosure { + let theme = themes?.filter({ $0 }) { // expected-error {{value of type 'MyStruct' has no member 'filter'}} + _ = theme + } + } + } +}