Skip to content

[5.1][CSDiag] Always find and set correct declaration context for sub-expr type-check #25567

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 57 additions & 52 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,11 +408,9 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
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,
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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<ClosureExpr>(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<ClosureExpr>(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<bool, Expr *> walkToExprPre(Expr *E) override {
if (E == SubExpr) {
DC = CurrDC;
return {false, nullptr};
}

if (auto *closure = dyn_cast<ClosureExpr>(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<ClosureExpr>(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<DeclContext *> 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
Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %target-typecheck-verify-swift

protocol P {
associatedtype T : P
}

struct Generic<T> {
init(_ value: T) {}
}

@_functionBuilder
struct Builder {
static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1)
-> Generic<(C0, C1)> where C0 : P, C1 : P {
return Generic((c0, c1))
}
}

struct G<C> {
init(@Builder _: () -> C) {}
}

struct Empty {
init() {}
}

struct Test<T> where T : P {
init(@Builder _: () -> T) {}
}

let x = G {
Empty()
Test { <#code#> } // expected-error {{editor placeholder in source file}}
}