diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index badfe3fcadbd8..e5dc96be7e151 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -251,9 +251,15 @@ getImplicitMemberReferenceAccessSemantics(Expr *base, VarDecl *member, return member->getAccessSemanticsFromContext(DC); } -void ConstraintSystem::propagateLValueAccessKind(Expr *E, - AccessKind accessKind, +void ConstraintSystem::propagateLValueAccessKind(Expr *E, AccessKind accessKind, + bool isShallow, bool allowOverwrite) { + // If solver is set up in "shallow" mode we expect that some of the + // sub-expressions are already type-checked which means that they + // already have access kind set. + if (isShallow && E->hasLValueAccessKind() && !allowOverwrite) + return; + E->propagateLValueAccessKind(accessKind, [&](Expr *E) -> Type { return getType(E); @@ -434,6 +440,7 @@ namespace { DeclContext *dc; const Solution &solution; bool SuppressDiagnostics; + bool IsShallow; /// Recognize used conformances from an imported type when we must emit /// the witness table. @@ -885,7 +892,8 @@ namespace { // will handle this. if (record.OpaqueValue && record.OpaqueValue->hasLValueAccessKind()) cs.propagateLValueAccessKind(record.ExistentialValue, - record.OpaqueValue->getLValueAccessKind()); + record.OpaqueValue->getLValueAccessKind(), + IsShallow); // Form the open-existential expression. result = new (tc.Context) OpenExistentialExpr( @@ -1846,9 +1854,9 @@ namespace { public: ExprRewriter(ConstraintSystem &cs, const Solution &solution, - bool suppressDiagnostics) - : cs(cs), dc(cs.DC), solution(solution), - SuppressDiagnostics(suppressDiagnostics) { } + bool suppressDiagnostics, bool shallow = false) + : cs(cs), dc(cs.DC), solution(solution), + SuppressDiagnostics(suppressDiagnostics), IsShallow(shallow) {} ConstraintSystem &getConstraintSystem() const { return cs; } @@ -3095,7 +3103,8 @@ namespace { // case (when we turn the inout into an UnsafePointer) than to try to // discover that we're in that case right now. if (!cs.getType(expr->getSubExpr())->is()) - cs.propagateLValueAccessKind(expr->getSubExpr(), AccessKind::ReadWrite); + cs.propagateLValueAccessKind(expr->getSubExpr(), AccessKind::ReadWrite, + IsShallow); auto objectTy = cs.getType(expr->getSubExpr())->getRValueType(); // The type is simply inout of whatever the lvalue's object type was. @@ -3805,7 +3814,8 @@ namespace { auto destTy = cs.computeAssignDestType(expr->getDest(), expr->getLoc()); if (!destTy) return nullptr; - cs.propagateLValueAccessKind(expr->getDest(), AccessKind::Write); + cs.propagateLValueAccessKind(expr->getDest(), AccessKind::Write, + IsShallow); // Convert the source to the simplified destination type. auto locator = @@ -4114,7 +4124,7 @@ namespace { if (cs.getType(subExpr)->hasLValueType()) { // Treat this like a read of the property. - cs.propagateLValueAccessKind(subExpr, AccessKind::Read); + cs.propagateLValueAccessKind(subExpr, AccessKind::Read, IsShallow); } // Check that we requested a property getter or setter. @@ -6341,7 +6351,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, // Load from the lvalue. If we're loading the result of a force, // swap the order so that we load first and force the result. - cs.propagateLValueAccessKind(expr, AccessKind::Read); + cs.propagateLValueAccessKind(expr, AccessKind::Read, IsShallow); if (auto *forceExpr = dyn_cast(expr)) { fromType = cs.getType(forceExpr->getSubExpr())->getRValueType(); auto *loadExpr = cs.cacheType( @@ -6454,8 +6464,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, // Overwrite the l-value access kind to be read-only if we're // converting to a non-mutable pointer type. auto *E = cast(expr->getValueProvidingExpr())->getSubExpr(); - cs.propagateLValueAccessKind(E, - AccessKind::Read, /*overwrite*/ true); + cs.propagateLValueAccessKind(E, AccessKind::Read, IsShallow, + /*overwrite*/ true); } tc.requirePointerArgumentIntrinsics(expr->getLoc()); @@ -6558,7 +6568,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, // In an 'inout' operator like "i += 1", the operand is converted from // an implicit lvalue to an inout argument. assert(toIO->getObjectType()->isEqual(fromLValue->getObjectType())); - cs.propagateLValueAccessKind(expr, AccessKind::ReadWrite); + cs.propagateLValueAccessKind(expr, AccessKind::ReadWrite, IsShallow); return cs.cacheType(new (tc.Context) InOutExpr(expr->getStartLoc(), expr, toIO->getObjectType(), @@ -6577,7 +6587,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, if (performLoad) { // Load from the lvalue. - cs.propagateLValueAccessKind(expr, AccessKind::Read); + cs.propagateLValueAccessKind(expr, AccessKind::Read, IsShallow); expr = cs.cacheType(new (tc.Context) LoadExpr(expr, fromLValue->getObjectType())); @@ -6834,7 +6844,7 @@ ExprRewriter::coerceObjectArgumentToType(Expr *expr, // Use InOutExpr to convert it to an explicit inout argument for the // receiver. - cs.propagateLValueAccessKind(expr, AccessKind::ReadWrite); + cs.propagateLValueAccessKind(expr, AccessKind::ReadWrite, IsShallow); return cs.cacheType(new (ctx) InOutExpr(expr->getStartLoc(), expr, toInOutTy->getInOutObjectType(), /*isImplicit*/ true)); @@ -8007,7 +8017,7 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, Expr *ConstraintSystem::applySolutionShallow(const Solution &solution, Expr *expr, bool suppressDiagnostics) { - ExprRewriter rewriter(*this, solution, suppressDiagnostics); + ExprRewriter rewriter(*this, solution, suppressDiagnostics, true); rewriter.walkToExprPre(expr); Expr *result = rewriter.walkToExprPost(expr); if (result) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 27db3d8e872fd..155a3a5ecaa88 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -2031,8 +2031,7 @@ class ConstraintSystem { /// Call Expr::propagateLValueAccessKind on the given expression, /// using a custom accessor for the type on the expression that /// reads the type from the ConstraintSystem expression type map. - void propagateLValueAccessKind(Expr *E, - AccessKind accessKind, + void propagateLValueAccessKind(Expr *E, AccessKind accessKind, bool isShallow, bool allowOverwrite = false); /// Call Expr::isTypeReference on the given expression, using a diff --git a/validation-test/compiler_crashers_2_fixed/0146-rdar38309176.swift b/validation-test/compiler_crashers_2_fixed/0146-rdar38309176.swift new file mode 100644 index 0000000000000..9f580a6276b9f --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/0146-rdar38309176.swift @@ -0,0 +1,9 @@ +// RUN: %target-typecheck-verify-swift %s + +func foo(_ msg: Int) {} +func foo(_ msg: Double) {} + +func rdar38309176(_ errors: inout [String]) { + foo("error: \(errors[0])") // expected-error {{cannot invoke 'foo' with an argument list of type '(String)'}} + // expected-note@-1 {{overloads for 'foo' exist with these partially matching parameter lists: (Int), (Double)}} +}