diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 2602ade933d0d..3573a8cfb3c59 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -466,6 +466,9 @@ class TypeVariableType::Implementation { /// Determine whether this type variable represents a closure type. bool isClosureType() const; + /// Determine whether this type variable represents a type of tap expression. + bool isTapType() const; + /// Determine whether this type variable represents one of the /// parameter types associated with a closure. bool isClosureParameterType() const; @@ -3865,6 +3868,20 @@ class ConstraintSystem { bool resolveClosure(TypeVariableType *typeVar, Type contextualType, ConstraintLocatorBuilder locator); + /// Bind tap expression to the given contextual type and generate + /// constraints for its body. + /// + /// \param typeVar The type variable representing the tap expression. + /// \param contextualType The contextual type this tap expression + /// would be bound to. + /// \param locator The locator associated with contextual type. + /// + /// \returns `true` if it was possible to generate constraints for + /// the body and assign fixed type to the tap expression, `false` + /// otherwise. + bool resolveTapBody(TypeVariableType *typeVar, Type contextualType, + ConstraintLocatorBuilder locator); + /// Assign a fixed type to the given type variable. /// /// \param typeVar The type variable to bind. @@ -4259,6 +4276,14 @@ class ConstraintSystem { FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); + /// Generate constraints for the body of the given tap expression. + /// + /// \param tap the tap expression + /// + /// \returns \c true if constraint generation failed, \c false otherwise + [[nodiscard]] + bool generateConstraints(TapExpr *tap); + /// Generate constraints for the body of the given function or closure. /// /// \param fn The function or closure expression @@ -5174,6 +5199,21 @@ class ConstraintSystem { std::function(SyntacticElementTarget)> rewriteTarget); + /// Apply the given solution to the given tap expression. + /// + /// \param solution The solution to apply. + /// \param tapExpr The tap expression to which the solution is being applied. + /// \param currentDC The declaration context in which transformations + /// will be applied. + /// \param rewriteTarget Function that performs a rewrite of any + /// solution application target within the context. + /// + /// \returns true if solution cannot be applied. + bool applySolutionToBody( + Solution &solution, TapExpr *tapExpr, DeclContext *¤tDC, + std::function(SyntacticElementTarget)> + rewriteTarget); + /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully /// resolved before any others. diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 19b8cd4d83d7b..9b494a1f94287 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8451,15 +8451,11 @@ namespace { class ExprWalker : public ASTWalker { ExprRewriter &Rewriter; SmallVector ClosuresToTypeCheck; - SmallVector, 4> TapsToTypeCheck; public: ExprWalker(ExprRewriter &Rewriter) : Rewriter(Rewriter) { } - ~ExprWalker() { - assert(ClosuresToTypeCheck.empty()); - assert(TapsToTypeCheck.empty()); - } + ~ExprWalker() { assert(ClosuresToTypeCheck.empty()); } bool shouldWalkIntoPropertyWrapperPlaceholderValue() override { // Property wrapper placeholder underlying values are filled in @@ -8468,9 +8464,7 @@ namespace { } /// Check if there are any closures or tap expressions left to process separately. - bool hasDelayedTasks() { - return !ClosuresToTypeCheck.empty() || !TapsToTypeCheck.empty(); - } + bool hasDelayedTasks() { return !ClosuresToTypeCheck.empty(); } /// Process delayed closure bodies and `Tap` expressions. /// @@ -8513,17 +8507,6 @@ namespace { hadError |= TypeChecker::typeCheckClosureBody(closure); } - // Tap expressions too; they should or should not be - // type-checked under the same conditions as closure bodies. - { - for (const auto &tuple : TapsToTypeCheck) { - auto tap = std::get<0>(tuple); - auto tapDC = std::get<1>(tuple); - hadError |= TypeChecker::typeCheckTapBody(tap, tapDC); - } - TapsToTypeCheck.clear(); - } - return hadError; } @@ -8548,10 +8531,9 @@ namespace { return Action::SkipChildren(SVE); } - if (auto tap = dyn_cast(expr)) { - // We remember the DeclContext because the code to handle - // single-expression-body closures above changes it. - TapsToTypeCheck.push_back(std::make_pair(tap, Rewriter.dc)); + if (auto tap = dyn_cast_or_null(expr)) { + rewriteTapExpr(tap); + return Action::SkipChildren(tap); } if (auto captureList = dyn_cast(expr)) { @@ -8694,6 +8676,26 @@ namespace { if (auto expr = resultTarget->getAsExpr()) solution.setExprTypes(expr); + return resultTarget; + }); + } + + void rewriteTapExpr(TapExpr *tap) { + auto &solution = Rewriter.solution; + + // First, let's visit the tap expression itself + // and set all of the inferred types. + Rewriter.visitTapExpr(tap); + + // Now, let's apply solution to the body + (void)Rewriter.cs.applySolutionToBody( + solution, tap, Rewriter.dc, [&](SyntacticElementTarget target) { + auto resultTarget = rewriteTarget(target); + if (resultTarget) { + if (auto expr = resultTarget->getAsExpr()) + solution.setExprTypes(expr); + } + return resultTarget; }); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index d4d3811d8bde8..f77c641435d5f 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -848,6 +848,65 @@ namespace { namespace { +// Collect any variable references whose types involve type variables, +// because there will be a dependency on those type variables once we have +// generated constraints for the closure/tap body. This includes references +// to other closure params such as in `{ x in { x }}` where the inner +// closure is dependent on the outer closure's param type, as well as +// cases like `for i in x where bar({ i })` where there's a dependency on +// the type variable for the pattern `i`. +struct VarRefCollector : public ASTWalker { + ConstraintSystem &cs; + llvm::SmallPtrSet varRefs; + + VarRefCollector(ConstraintSystem &cs) : cs(cs) {} + + bool shouldWalkCaptureInitializerExpressions() override { return true; } + + MacroWalking getMacroWalkingBehavior() const override { + return MacroWalking::Arguments; + } + + PreWalkResult walkToExprPre(Expr *expr) override { + // Retrieve type variables from references to var decls. + if (auto *declRef = dyn_cast(expr)) { + if (auto *varDecl = dyn_cast(declRef->getDecl())) { + if (auto varType = cs.getTypeIfAvailable(varDecl)) { + varType->getTypeVariables(varRefs); + } + } + } + + // FIXME: We can see UnresolvedDeclRefExprs here because we have + // not yet run preCheckExpression() on the entire closure body + // yet. + // + // We could consider pre-checking more eagerly. + if (auto *declRef = dyn_cast(expr)) { + auto name = declRef->getName(); + auto loc = declRef->getLoc(); + if (name.isSimpleName() && loc.isValid()) { + auto *varDecl = + dyn_cast_or_null(ASTScope::lookupSingleLocalDecl( + cs.DC->getParentSourceFile(), name.getFullName(), loc)); + if (varDecl) + if (auto varType = cs.getTypeIfAvailable(varDecl)) + varType->getTypeVariables(varRefs); + } + } + + return Action::Continue(expr); + } + + PreWalkAction walkToDeclPre(Decl *D) override { + return Action::VisitChildrenIf(isa(D)); + } + + PreWalkResult walkToPatternPre(Pattern *P) override { + return Action::SkipChildren(P); + } +}; + class ConstraintGenerator : public ExprVisitor { ConstraintSystem &CS; DeclContext *CurDC; @@ -1199,15 +1258,38 @@ namespace { return nullptr; } - auto interpolationTV = DependentMemberType::get(tv, associatedTypeDecl); + auto interpolationTV = + CS.createTypeVariable(locator, TVO_CanBindToNoEscape); + auto interpolationType = + DependentMemberType::get(tv, associatedTypeDecl); + + CS.addConstraint(ConstraintKind::Equal, interpolationTV, + interpolationType, locator); auto appendingExprType = CS.getType(appendingExpr); auto appendingLocator = CS.getConstraintLocator(appendingExpr); - // Must be Conversion; if it's Equal, then in semi-rare cases, the + SmallVector referencedVars; + + if (auto *tap = getAsExpr(appendingExpr)) { + // Collect all of the variable references that appear + // in the tap body, otherwise tap expression is going + // to get disconnected from the context. + if (auto *body = tap->getBody()) { + VarRefCollector refCollector(CS); + + body->walk(refCollector); + + referencedVars.append(refCollector.varRefs.begin(), + refCollector.varRefs.end()); + } + } + + // Must be Conversion; if it's Equal, then in semi-rare cases, the // interpolation temporary variable cannot be @lvalue. - CS.addConstraint(ConstraintKind::Conversion, appendingExprType, - interpolationTV, appendingLocator); + CS.addUnsolvedConstraint(Constraint::create( + CS, ConstraintKind::Conversion, appendingExprType, interpolationTV, + appendingLocator, referencedVars)); } return tv; @@ -2934,72 +3016,14 @@ namespace { auto *locator = CS.getConstraintLocator(closure); auto closureType = CS.createTypeVariable(locator, TVO_CanBindToNoEscape); - // Collect any variable references whose types involve type variables, - // because there will be a dependency on those type variables once we have - // generated constraints for the closure body. This includes references - // to other closure params such as in `{ x in { x }}` where the inner - // closure is dependent on the outer closure's param type, as well as - // cases like `for i in x where bar({ i })` where there's a dependency on - // the type variable for the pattern `i`. - struct CollectVarRefs : public ASTWalker { - ConstraintSystem &cs; - llvm::SmallPtrSet varRefs; - - CollectVarRefs(ConstraintSystem &cs) : cs(cs) { } - - bool shouldWalkCaptureInitializerExpressions() override { return true; } - - MacroWalking getMacroWalkingBehavior() const override { - return MacroWalking::Arguments; - } - - PreWalkResult walkToExprPre(Expr *expr) override { - // Retrieve type variables from references to var decls. - if (auto *declRef = dyn_cast(expr)) { - if (auto *varDecl = dyn_cast(declRef->getDecl())) { - if (auto varType = cs.getTypeIfAvailable(varDecl)) { - varType->getTypeVariables(varRefs); - } - } - } - - // FIXME: We can see UnresolvedDeclRefExprs here because we have - // not yet run preCheckExpression() on the entire closure body - // yet. - // - // We could consider pre-checking more eagerly. - if (auto *declRef = dyn_cast(expr)) { - auto name = declRef->getName(); - auto loc = declRef->getLoc(); - if (name.isSimpleName() && loc.isValid()) { - auto *varDecl = dyn_cast_or_null( - ASTScope::lookupSingleLocalDecl(cs.DC->getParentSourceFile(), - name.getFullName(), loc)); - if (varDecl) - if (auto varType = cs.getTypeIfAvailable(varDecl)) - varType->getTypeVariables(varRefs); - } - } - - return Action::Continue(expr); - } - - PreWalkAction walkToDeclPre(Decl *D) override { - return Action::VisitChildrenIf(isa(D)); - } - - PreWalkResult walkToPatternPre(Pattern *P) override { - return Action::SkipChildren(P); - } - } collectVarRefs(CS); - + VarRefCollector refCollector(CS); // Walk the capture list if this closure has one, because it could // reference declarations from the outer closure. if (auto *captureList = getAsExpr(CS.getParentExpr(closure))) { - captureList->walk(collectVarRefs); + captureList->walk(refCollector); } else { - closure->walk(collectVarRefs); + closure->walk(refCollector); } auto inferredType = inferClosureType(closure); @@ -3007,7 +3031,7 @@ namespace { return Type(); SmallVector referencedVars{ - collectVarRefs.varRefs.begin(), collectVarRefs.varRefs.end()}; + refCollector.varRefs.begin(), refCollector.varRefs.end()}; CS.addUnsolvedConstraint(Constraint::create( CS, ConstraintKind::DefaultClosureType, closureType, inferredType, diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index d9f1d46a238df..99350f0025a89 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4379,6 +4379,12 @@ ConstraintSystem::matchTypesBindTypeVar( : getTypeMatchFailure(locator); } + if (typeVar->getImpl().isTapType()) { + return resolveTapBody(typeVar, type, locator) + ? getTypeMatchSuccess() + : getTypeMatchFailure(locator); + } + assignFixedType(typeVar, type, /*updateState=*/true, /*notifyInference=*/!flags.contains(TMF_BindingTypeVariable)); @@ -11222,6 +11228,23 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, return !generateConstraints(AnyFunctionRef{closure}, closure->getBody()); } +bool ConstraintSystem::resolveTapBody(TypeVariableType *typeVar, + Type contextualType, + ConstraintLocatorBuilder locator) { + auto *tapLoc = typeVar->getImpl().getLocator(); + auto *tapExpr = castToExpr(tapLoc->getAnchor()); + + // Assign a type to tap expression itself. + assignFixedType(typeVar, contextualType, getConstraintLocator(locator)); + // Set type to `$interpolation` variable declared in the body of tap + // expression. + setType(tapExpr->getVar(), contextualType); + + // With all of the contextual information recorded in the constraint system, + // it's time to generate constraints for the body of this tap expression. + return !generateConstraints(tapExpr); +} + ConstraintSystem::SolutionKind ConstraintSystem::simplifyDynamicTypeOfConstraint( Type type1, Type type2, diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 5fc06c2135c51..98a6ae9fe5310 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -351,6 +351,13 @@ static void createConjunction(ConstraintSystem &cs, isIsolated = true; } + if (locator->directlyAt()) { + // Body of the interpolation is always isolated from its context, only + // its individual elements are allowed access to type information + // from the outside e.g. external declaration references. + isIsolated = true; + } + UnresolvedVarCollector paramCollector(cs); for (const auto &entry : elements) { @@ -402,13 +409,15 @@ ElementInfo makeJoinElement(ConstraintSystem &cs, TypeJoinExpr *join, struct SyntacticElementContext : public llvm::PointerUnion { + SingleValueStmtExpr *, TapExpr *> { // Inherit the constructors from PointerUnion. using PointerUnion::PointerUnion; /// A join that should be applied to the elements of a SingleValueStmtExpr. NullablePtr ElementJoin; + static SyntacticElementContext forTapExpr(TapExpr *tap) { return {tap}; } + static SyntacticElementContext forFunctionRef(AnyFunctionRef ref) { if (auto *decl = ref.getAbstractFunctionDecl()) { return {decl}; @@ -440,6 +449,8 @@ struct SyntacticElementContext return closure; } else if (auto *SVE = dyn_cast()) { return SVE->getDeclContext(); + } else if (auto *tap = this->dyn_cast()) { + return tap->getVar()->getDeclContext(); } else { llvm_unreachable("unsupported kind"); } @@ -475,6 +486,8 @@ struct SyntacticElementContext return closure->getBody(); } else if (auto *SVE = dyn_cast()) { return SVE->getStmt(); + } else if (auto *tap = this->dyn_cast()) { + return tap->getBody(); } else { llvm_unreachable("unsupported kind"); } @@ -1327,6 +1340,22 @@ class SyntacticElementConstraintGenerator }; } +bool ConstraintSystem::generateConstraints(TapExpr *tap) { + SyntacticElementConstraintGenerator generator( + *this, SyntacticElementContext::forTapExpr(tap), + getConstraintLocator(tap)); + + auto *body = tap->getBody(); + + if (!body) { + assert(tap->getSubExpr()); + return false; + } + + generator.visit(tap->getBody()); + return generator.hadError; +} + bool ConstraintSystem::generateConstraints(AnyFunctionRef fn, BraceStmt *body) { NullablePtr locator; @@ -1480,6 +1509,8 @@ ConstraintSystem::simplifySyntacticElementConstraint( context = SyntacticElementContext::forFunction(fn); } else if (auto *SVE = getAsExpr(anchor)) { context = SyntacticElementContext::forSingleValueStmtExpr(SVE); + } else if (auto *tap = getAsExpr(anchor)) { + context = SyntacticElementContext::forTapExpr(tap); } else { return SolutionKind::Error; } @@ -1543,7 +1574,6 @@ class SyntacticElementSolutionApplication protected: Solution &solution; SyntacticElementContext context; - Type resultType; RewriteTargetFn rewriteTarget; /// All `func`s declared in the body of the closure. @@ -1556,24 +1586,39 @@ class SyntacticElementSolutionApplication SyntacticElementSolutionApplication(Solution &solution, SyntacticElementContext context, RewriteTargetFn rewriteTarget) - : solution(solution), context(context), rewriteTarget(rewriteTarget) { - if (auto fn = AnyFunctionRef::fromDeclContext(context.getAsDeclContext())) { + : solution(solution), context(context), rewriteTarget(rewriteTarget) {} + + virtual ~SyntacticElementSolutionApplication() {} + +private: + Type getContextualResultType() const { + // Taps do not have a contextual result type. + if (context.is()) { + return Type(); + } + + auto fn = context.getAsAnyFunctionRef(); + + if (context.is()) { + // if/switch expressions can have `return` inside. + fn = AnyFunctionRef::fromDeclContext(context.getAsDeclContext()); + } + + if (fn) { if (auto transform = solution.getAppliedBuilderTransform(*fn)) { - resultType = solution.simplifyType(transform->bodyResultType); + return solution.simplifyType(transform->bodyResultType); } else if (auto *closure = getAsExpr(fn->getAbstractClosureExpr())) { - resultType = solution.getResolvedType(closure) - ->castTo() - ->getResult(); + return solution.getResolvedType(closure) + ->castTo() + ->getResult(); } else { - resultType = fn->getBodyResultType(); + return fn->getBodyResultType(); } } - } - - virtual ~SyntacticElementSolutionApplication() {} -private: + return Type(); + } ASTNode visit(Stmt *S, bool performSyntacticDiagnostics = true) { auto rewritten = ASTVisitor::visit(S); @@ -1929,17 +1974,18 @@ class SyntacticElementSolutionApplication auto closure = context.getAsAbstractClosureExpr(); if (closure && !closure.get()->hasSingleExpressionBody() && closure.get()->getBody() == braceStmt) { + auto resultType = getContextualResultType(); if (resultType->getOptionalObjectType() && resultType->lookThroughAllOptionalTypes()->isVoid() && !braceStmt->getLastElement().isStmt(StmtKind::Return)) { - return addImplicitVoidReturn(braceStmt); + return addImplicitVoidReturn(braceStmt, resultType); } } return braceStmt; } - ASTNode addImplicitVoidReturn(BraceStmt *braceStmt) { + ASTNode addImplicitVoidReturn(BraceStmt *braceStmt, Type contextualResultTy) { auto &cs = solution.getConstraintSystem(); auto &ctx = cs.getASTContext(); @@ -1954,7 +2000,7 @@ class SyntacticElementSolutionApplication // number of times. { SyntacticElementTarget target(resultExpr, context.getAsDeclContext(), - CTP_ReturnStmt, resultType, + CTP_ReturnStmt, contextualResultTy, /*isDiscarded=*/false); cs.setTargetFor(returnStmt, target); @@ -1975,6 +2021,8 @@ class SyntacticElementSolutionApplication ASTNode visitReturnStmt(ReturnStmt *returnStmt) { auto &cs = solution.getConstraintSystem(); + auto resultType = getContextualResultType(); + if (!returnStmt->hasResult()) { // If contextual is not optional, there is nothing to do here. if (resultType->isVoid()) @@ -2468,8 +2516,23 @@ bool ConstraintSystem::applySolutionToBody(Solution &solution, return false; } +bool ConstraintSystem::applySolutionToBody(Solution &solution, TapExpr *tapExpr, + DeclContext *¤tDC, + RewriteTargetFn rewriteTarget) { + SyntacticElementSolutionApplication application( + solution, SyntacticElementContext::forTapExpr(tapExpr), rewriteTarget); + + auto body = application.apply(); + + if (!body || application.hadError) + return true; + + tapExpr->setBody(castToStmt(body)); + return false; +} + bool ConjunctionElement::mightContainCodeCompletionToken( - const ConstraintSystem &cs) const { + const ConstraintSystem &cs) const { if (Element->getKind() == ConstraintKind::SyntacticElement) { if (Element->getSyntacticElement().getSourceRange().isInvalid()) { return true; diff --git a/lib/Sema/ConstantnessSemaDiagnostics.cpp b/lib/Sema/ConstantnessSemaDiagnostics.cpp index 9f39f0bc3cf36..06f2599f4e7ac 100644 --- a/lib/Sema/ConstantnessSemaDiagnostics.cpp +++ b/lib/Sema/ConstantnessSemaDiagnostics.cpp @@ -353,14 +353,7 @@ void swift::diagnoseConstantArgumentRequirement( return walkToClosureExprPre(closureExpr); } - // Interpolated expressions' bodies will be type checked - // separately so exit early to avoid duplicate diagnostics. - // The caveat is that they won't be checked inside closure - // bodies because we manually check all closures to avoid - // duplicate diagnostics. Therefore we must still descend into - // interpolated expressions if we are inside of a closure. - if (!expr || isa(expr) || !expr->getType() || - (isa(expr) && !insideClosure)) + if (!expr || isa(expr) || !expr->getType()) return Action::SkipChildren(expr); if (auto *callExpr = dyn_cast(expr)) { diagnoseConstantArgumentRequirementOfCall(callExpr, DC->getASTContext()); diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 09a7473999eae..5de305b05d970 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -136,8 +136,6 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - PreWalkResult walkToExprPre(Expr *E) override { if (auto collection = dyn_cast(E)) { if (collection->isTypeDefaulted()) { @@ -1456,8 +1454,6 @@ static void diagRecursivePropertyAccess(const Expr *E, const DeclContext *DC) { bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -1757,8 +1753,6 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - PreWalkResult walkToExprPre(Expr *E) override { if (auto *CE = dyn_cast(E)) { // If this is a potentially-escaping closure expression, start looking @@ -4175,8 +4169,6 @@ static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Expr *E) { bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -4305,8 +4297,6 @@ class ObjCSelectorWalker : public ASTWalker { bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -5166,8 +5156,6 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E, bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -5247,8 +5235,6 @@ static void diagnoseDeprecatedWritableKeyPath(const Expr *E, bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -5543,8 +5529,6 @@ static void diagUnqualifiedAccessToMethodNamedSelf(const Expr *E, return false; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -5704,8 +5688,6 @@ diagnoseDictionaryLiteralDuplicateKeyEntries(const Expr *E, return false; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index f9af504fb7e0d..1ef8c3aa21e98 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -3232,8 +3232,6 @@ class ExprAvailabilityWalker : public ASTWalker { return false; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::ArgumentsAndExpansion; } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 67b0a9534a382..26a6d1040c130 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -2029,8 +2029,6 @@ namespace { bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return true; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 680e55f17764b..f1f1a8762b851 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -116,6 +116,10 @@ bool TypeVariableType::Implementation::isClosureType() const { return isExpr(locator->getAnchor()) && locator->getPath().empty(); } +bool TypeVariableType::Implementation::isTapType() const { + return locator && locator->directlyAt(); +} + bool TypeVariableType::Implementation::isClosureParameterType() const { if (!(locator && locator->getAnchor())) return false; diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 5e474a26eae3f..6e5433dc6dbac 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -624,7 +624,10 @@ extension P_47606 { let u = rdar33296619().element //expected-error {{cannot find 'rdar33296619' in scope}} [1].forEach { _ in - _ = "\(u)" + _ = "\(u)" // No diagnostic because `u` is already diagnosed and marked as invalid +} + +[1].forEach { _ in _ = 1 + "hi" // expected-error {{binary operator '+' cannot be applied to operands of type 'Int' and 'String'}} // expected-note@-1 {{overloads for '+' exist with these partially matching parameter lists: (Int, Int), (String, String)}} } diff --git a/test/Constraints/interpolation_segments.swift b/test/Constraints/interpolation_segments.swift deleted file mode 100644 index 4c2e02ee7a9c2..0000000000000 --- a/test/Constraints/interpolation_segments.swift +++ /dev/null @@ -1,21 +0,0 @@ -// RUN: %target-typecheck-verify-swift -debug-constraints > %t.dump 2>&1 -// RUN: %FileCheck %s < %t.dump - -// Make sure that the type checker doesn't initially try to solve the appendLiteral and -// appendInterpolation calls. Specifically, we check that the string literal -// has been assigned a type variable, but the calls inside the body have not. - -// CHECK: ---Initial constraints for the given expression--- -// CHECK: (interpolated_string_literal_expr type='$T -// CHECK-NOT: (call_expr implicit type='$T -// CHECK: ---Solution--- - -// We also check that the type checker did not need to evaluate any -// DefaultStringInterpolation overloads in the initial expression. - -// CHECK-NOT: Constraint restrictions: -// CHECK: --- Solution #{{.*}}--- -// CHECK: Overload choices: -// CHECK: Swift.(file).DefaultStringInterpolation.append - -_ = "\(1), \(2), \(3), \(4)" diff --git a/test/Sema/string_interpolations.swift b/test/Sema/string_interpolations.swift new file mode 100644 index 0000000000000..94f82c529bfab --- /dev/null +++ b/test/Sema/string_interpolations.swift @@ -0,0 +1,58 @@ +// RUN: %target-typecheck-verify-swift + +do { + enum E: String, CaseIterable { + static var allCases: [E] { + [.one, .two] + } + + case one + case two + + func test(id: String) { + for c in Self.allCases where id == "\(c)" { // Ok + } + } + } +} + +do { + struct Data { + var text: String + + static func fn(_: (inout Data) -> Void) {} + static func fn(_: (inout Int) -> Void) {} + } + + func test(_: @autoclosure () -> T) { + } + + test((1...3).map { number in Data.fn { $0.text = "\(number)" } }) + + func test_multi(_: () -> T) { + } + + test_multi { + let x = 1...3 + _ = x.map { number in + Data.fn { + if $0.text == "\(number)" { + $0.text = "\(x)" + } + } + } + } +} + +// This case is (currently) interesting because "\(query)" is type-checked +// separately as part of ~= operator application. +func test_interpolation_in_case(query: String) { + _ = { (request: String) in + switch request { + case "\(query)": // Ok + break + default: + break + } + } +}