diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 00136b7d0b483..aabdb05839e1c 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -408,11 +408,9 @@ class FailureDiagnosis :public ASTVisitor{ return e ? CS.getType(e) : Type(); } - /// This is the same as typeCheckChildIndependently, but works on an arbitrary - /// subexpression of the current node because it handles ClosureExpr parents - /// of the specified node. - Expr *typeCheckArbitrarySubExprIndependently(Expr *subExpr, - TCCOptions options = TCCOptions()); + /// Find a nearest declaration context which could be used + /// to type-check this sub-expression. + DeclContext *findDeclContext(Expr *subExpr) const; /// Special magic to handle inout exprs and tuples in argument lists. Expr *typeCheckArgumentChildIndependently(Expr *argExpr, Type argType, @@ -1150,8 +1148,8 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){ // constraint. if (CS.getContextualTypePurpose() != CTP_Unused) options |= TCC_ForceRecheck; - - auto sub = typeCheckArbitrarySubExprIndependently(anchor, options); + + auto sub = typeCheckChildIndependently(anchor, options); if (!sub) return true; fromType = CS.getType(sub); } @@ -1624,10 +1622,14 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( if ((!convertType || options.contains(TCC_AllowUnresolvedTypeVariables)) && allowFreeTypeVariables) TCEOptions |= TypeCheckExprFlags::AllowUnresolvedTypeVariables; - - auto resultTy = CS.TC.typeCheckExpression( - subExpr, CS.DC, TypeLoc::withoutLoc(convertType), convertTypePurpose, - TCEOptions, listener, &CS); + + // When we're type checking a single-expression closure, we need to reset the + // DeclContext to this closure for the recursive type checking. Otherwise, + // if there is a closure in the subexpression, we can violate invariants. + auto *DC = findDeclContext(subExpr); + auto resultTy = + CS.TC.typeCheckExpression(subExpr, DC, TypeLoc::withoutLoc(convertType), + convertTypePurpose, TCEOptions, listener, &CS); CS.cacheExprTypes(subExpr); @@ -1663,49 +1665,53 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( return subExpr; } -/// This is the same as typeCheckChildIndependently, but works on an arbitrary -/// subexpression of the current node because it handles ClosureExpr parents -/// of the specified node. -Expr *FailureDiagnosis:: -typeCheckArbitrarySubExprIndependently(Expr *subExpr, TCCOptions options) { - if (subExpr == expr) - return typeCheckChildIndependently(subExpr, options); - - // Construct a parent map for the expr tree we're investigating. - auto parentMap = expr->getParentMap(); - - ClosureExpr *NearestClosure = nullptr; - - // Walk the parents of the specified expression, handling any ClosureExprs. - for (Expr *node = parentMap[subExpr]; node; node = parentMap[node]) { - auto *CE = dyn_cast(node); - if (!CE) continue; - - // Keep track of the innermost closure we see that we're jumping into. - if (!NearestClosure) - NearestClosure = CE; - - // If we have a ClosureExpr parent of the specified node, check to make sure - // none of its arguments are type variables. If so, these type variables - // would be accessible to name lookup of the subexpression and may thus leak - // in. Reset them to UnresolvedTypes for safe measures. - for (auto *param : *CE->getParameters()) { - if (param->hasValidSignature()) { - auto type = param->getType(); - assert(!type->hasTypeVariable() && !type->hasError()); - (void)type; +DeclContext *FailureDiagnosis::findDeclContext(Expr *subExpr) const { + if (auto *closure = + dyn_cast(subExpr->getSemanticsProvidingExpr())) + return closure->getParent(); + + struct DCFinder : public ASTWalker { + DeclContext *DC, *CurrDC; + Expr *SubExpr; + + DCFinder(DeclContext *DC, Expr *expr) : DC(DC), CurrDC(DC), SubExpr(expr) {} + + std::pair walkToExprPre(Expr *E) override { + if (E == SubExpr) { + DC = CurrDC; + return {false, nullptr}; + } + + if (auto *closure = dyn_cast(E)) { + CurrDC = closure; + // If we have a ClosureExpr parent of the specified node, check to make + // sure none of its arguments are type variables. If so, these type + // variables would be accessible to name lookup of the subexpression and + // may thus leak in. Reset them to UnresolvedTypes for safe measures. + assert(llvm::all_of(*closure->getParameters(), [](const ParamDecl *PD) { + if (PD->hasValidSignature()) { + auto paramTy = PD->getType(); + return !(paramTy->hasTypeVariable() || paramTy->hasError()); + } + return true; + })); + } + + return {true, E}; + } + + Expr *walkToExprPost(Expr *E) override { + if (auto *closure = dyn_cast(E)) { + assert(CurrDC == closure && "DeclContext imbalance"); + CurrDC = closure->getParent(); } + return E; } - } - // When we're type checking a single-expression closure, we need to reset the - // DeclContext to this closure for the recursive type checking. Otherwise, - // if there is a closure in the subexpression, we can violate invariants. - auto newDC = NearestClosure ? NearestClosure : CS.DC; - llvm::SaveAndRestore SavedDC(CS.DC, newDC); + } finder(CS.DC, subExpr); - // Otherwise, we're ok to type check the subexpr. - return typeCheckChildIndependently(subExpr, options); + expr->walk(finder); + return finder.DC; } /// For an expression being type checked with a CTP_CalleeResult contextual @@ -6723,8 +6729,7 @@ bool FailureDiagnosis::diagnoseMemberFailures( NameLoc = DeclNameLoc(memberRange.Start); // Retypecheck the anchor type, which is the base of the member expression. - baseExpr = - typeCheckArbitrarySubExprIndependently(baseExpr, TCC_AllowLValue); + baseExpr = typeCheckChildIndependently(baseExpr, TCC_AllowLValue); if (!baseExpr) return true; diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift new file mode 100644 index 0000000000000..9c3714615fab9 --- /dev/null +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar50869732.swift @@ -0,0 +1,34 @@ +// RUN: %target-typecheck-verify-swift + +protocol P { + associatedtype T : P +} + +struct Generic { + init(_ value: T) {} +} + +@_functionBuilder +struct Builder { + static func buildBlock(_ c0: C0, _ c1: C1) + -> Generic<(C0, C1)> where C0 : P, C1 : P { + return Generic((c0, c1)) + } +} + +struct G { + init(@Builder _: () -> C) {} +} + +struct Empty { + init() {} +} + +struct Test where T : P { + init(@Builder _: () -> T) {} +} + +let x = G { + Empty() + Test { <#code#> } // expected-error {{editor placeholder in source file}} +}