diff --git a/include/swift/Sema/CompletionContextFinder.h b/include/swift/Sema/CompletionContextFinder.h index 51dc3bfd515e2..dce6572908b7a 100644 --- a/include/swift/Sema/CompletionContextFinder.h +++ b/include/swift/Sema/CompletionContextFinder.h @@ -20,6 +20,10 @@ namespace swift { +namespace constraints { +class SyntacticElementTarget; +} + class CompletionContextFinder : public ASTWalker { enum class ContextKind { FallbackExpression, @@ -53,12 +57,9 @@ class CompletionContextFinder : public ASTWalker { return MacroWalking::Arguments; } - /// Finder for completion contexts within the provided initial expression. - CompletionContextFinder(ASTNode initialNode, DeclContext *DC) - : InitialExpr(initialNode.dyn_cast()), InitialDC(DC) { - assert(DC); - initialNode.walk(*this); - }; + /// Finder for completion contexts within the provided SyntacticElementTarget. + CompletionContextFinder(constraints::SyntacticElementTarget target, + DeclContext *DC); /// Finder for completion contexts within the outermost non-closure context of /// the code completion expression's direct context. diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 4af99948d5a94..bb3b00c9114e5 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -222,6 +222,9 @@ CUSTOM_LOCATOR_PATH_ELT(TernaryBranch) /// Performing a pattern patch. CUSTOM_LOCATOR_PATH_ELT(PatternMatch) +/// A conjunction for the ExprPatterns in a pattern. +SIMPLE_LOCATOR_PATH_ELT(ExprPatternConjunction) + /// Points to a particular attribute associated with one of /// the arguments e.g. `inout` or its type e.g. `@escaping`. /// diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 541540cc610bf..dd9f26bad17eb 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -706,6 +706,11 @@ T *getAsPattern(ASTNode node) { return nullptr; } +template +T *castToPattern(ASTNode node) { + return cast(node.get()); +} + template T *castToStmt(ASTNode node) { return cast(node.get()); } @@ -1515,6 +1520,10 @@ class Solution { llvm::SmallMapVector caseLabelItems; + /// A map of expressions to the ExprPatterns that they are being solved as + /// a part of. + llvm::SmallMapVector exprPatterns; + /// The set of parameters that have been inferred to be 'isolated'. llvm::SmallVector isolatedParams; @@ -1700,6 +1709,16 @@ class Solution { : nullptr; } + /// Retrieve the solved ExprPattern that corresponds to provided + /// sub-expression. + NullablePtr getExprPatternFor(Expr *E) const { + auto result = exprPatterns.find(E); + if (result == exprPatterns.end()) + return nullptr; + + return result->second; + } + /// This method implements functionality of `Expr::isTypeReference` /// with data provided by a given solution. bool isTypeReference(Expr *E) const; @@ -2163,6 +2182,10 @@ class ConstraintSystem { llvm::SmallMapVector caseLabelItems; + /// A map of expressions to the ExprPatterns that they are being solved as + /// a part of. + llvm::SmallMapVector exprPatterns; + /// The set of parameters that have been inferred to be 'isolated'. llvm::SmallSetVector isolatedParams; @@ -2754,6 +2777,9 @@ class ConstraintSystem { /// The length of \c caseLabelItems. unsigned numCaseLabelItems; + /// The length of \c exprPatterns. + unsigned numExprPatterns; + /// The length of \c isolatedParams. unsigned numIsolatedParams; @@ -3175,6 +3201,15 @@ class ConstraintSystem { caseLabelItems[item] = info; } + /// Record a given ExprPattern as the parent of its sub-expression. + void setExprPatternFor(Expr *E, ExprPattern *EP) { + assert(E); + assert(EP); + auto inserted = exprPatterns.insert({E, EP}).second; + assert(inserted && "Mapping already defined?"); + (void)inserted; + } + Optional getCaseLabelItemInfo( const CaseLabelItem *item) const { auto known = caseLabelItems.find(item); @@ -4299,6 +4334,11 @@ class ConstraintSystem { /// \returns \c true if constraint generation failed, \c false otherwise bool generateConstraints(SingleValueStmtExpr *E); + /// Generate constraints for an array of ExprPatterns, forming a conjunction + /// that solves each expression in turn. + void generateConstraints(ArrayRef exprPatterns, + ConstraintLocatorBuilder locator); + /// Generate constraints for the given (unchecked) expression. /// /// \returns a possibly-sanitized expression, or null if an error occurred. diff --git a/include/swift/Sema/SyntacticElementTarget.h b/include/swift/Sema/SyntacticElementTarget.h index 39ec72455dcd9..eab8edefe9ae0 100644 --- a/include/swift/Sema/SyntacticElementTarget.h +++ b/include/swift/Sema/SyntacticElementTarget.h @@ -190,13 +190,6 @@ class SyntacticElementTarget { ConstraintLocator *convertTypeLocator, bool isDiscarded); - SyntacticElementTarget(Expr *expr, DeclContext *dc, ExprPattern *pattern, - Type patternType) - : SyntacticElementTarget(expr, dc, CTP_ExprPattern, patternType, - /*isDiscarded=*/false) { - setPattern(pattern); - } - SyntacticElementTarget(ClosureExpr *closure, Type convertType) { kind = Kind::closure; this->closure.closure = closure; @@ -296,11 +289,8 @@ class SyntacticElementTarget { forPropertyWrapperInitializer(VarDecl *wrappedVar, DeclContext *dc, Expr *initializer); - static SyntacticElementTarget forExprPattern(Expr *expr, DeclContext *dc, - ExprPattern *pattern, - Type patternTy) { - return {expr, dc, pattern, patternTy}; - } + /// Form a target for the match expression of an ExprPattern. + static SyntacticElementTarget forExprPattern(ExprPattern *pattern); /// This is useful for code completion. ASTNode getAsASTNode() const { @@ -781,11 +771,13 @@ class SyntacticElementTarget { // For an initialization, include the pattern in the range too. if (isForInitialization()) { - if (auto patternRange = getInitializationPattern()->getSourceRange()) { - if (range.isInvalid()) { - range = patternRange; - } else { - range.widen(patternRange); + if (auto *pattern = getInitializationPattern()) { + if (auto patternRange = pattern->getSourceRange()) { + if (range.isInvalid()) { + range = patternRange; + } else { + range.widen(patternRange); + } } } } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 321a8b0729394..f845738fbfb7c 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4178,7 +4178,6 @@ GenericFunctionType *GenericFunctionType::get(GenericSignature sig, Type result, Optional info) { assert(sig && "no generic signature for generic function type?!"); - assert(!result->hasTypeVariable()); llvm::FoldingSetNodeID id; GenericFunctionType::Profile(id, sig, params, result, info); diff --git a/lib/IDE/TypeCheckCompletionCallback.cpp b/lib/IDE/TypeCheckCompletionCallback.cpp index d2f652bd84a68..9739a53fbd679 100644 --- a/lib/IDE/TypeCheckCompletionCallback.cpp +++ b/lib/IDE/TypeCheckCompletionCallback.cpp @@ -81,7 +81,13 @@ Type swift::ide::getTypeForCompletion(const constraints::Solution &S, /// \endcode /// If the code completion expression occurs in such an AST, return the /// declaration of the \c $match variable, otherwise return \c nullptr. -static VarDecl *getMatchVarIfInPatternMatch(Expr *E, ConstraintSystem &CS) { +static VarDecl *getMatchVarIfInPatternMatch(Expr *E, const Solution &S) { + if (auto EP = S.getExprPatternFor(E)) + return EP.get()->getMatchVar(); + + // TODO: Once ExprPattern type-checking is fully moved into the solver, + // the below can be deleted. + auto &CS = S.getConstraintSystem(); auto &Context = CS.getASTContext(); auto *Binary = dyn_cast_or_null(CS.getParentExpr(E)); @@ -109,20 +115,21 @@ static VarDecl *getMatchVarIfInPatternMatch(Expr *E, ConstraintSystem &CS) { } Type swift::ide::getPatternMatchType(const constraints::Solution &S, Expr *E) { - if (auto MatchVar = getMatchVarIfInPatternMatch(E, S.getConstraintSystem())) { - Type MatchVarType; - // If the MatchVar has an explicit type, it's not part of the solution. But - // we can look it up in the constraint system directly. - if (auto T = S.getConstraintSystem().getVarType(MatchVar)) { - MatchVarType = T; - } else { - MatchVarType = getTypeForCompletion(S, MatchVar); - } - if (MatchVarType) { - return MatchVarType; - } - } - return nullptr; + auto MatchVar = getMatchVarIfInPatternMatch(E, S); + if (!MatchVar) + return nullptr; + + if (S.hasType(MatchVar)) + return S.getResolvedType(MatchVar); + + // If the ExprPattern wasn't solved as part of the constraint system, it's + // not part of the solution. + // TODO: This can be removed once ExprPattern type-checking is fully part + // of the constraint system. + if (auto T = S.getConstraintSystem().getVarType(MatchVar)) + return T; + + return getTypeForCompletion(S, MatchVar); } void swift::ide::getSolutionSpecificVarTypes( diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index c187ad8428e34..79f0d0ec89857 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -2344,7 +2344,8 @@ Optional TypeChecker::applyResultBuilderBodyTransform( SmallVector solutions; cs.solveForCodeCompletion(solutions); - CompletionContextFinder analyzer(func, func->getDeclContext()); + SyntacticElementTarget funcTarget(func); + CompletionContextFinder analyzer(funcTarget, func->getDeclContext()); if (analyzer.hasCompletion()) { filterSolutionsForCodeCompletion(solutions, analyzer); for (const auto &solution : solutions) { diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 98f05458b3401..91caa86bf3df3 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8574,6 +8574,9 @@ namespace { return Action::SkipChildren(); } + NullablePtr + rewritePattern(Pattern *pattern, DeclContext *DC); + /// Rewrite the target, producing a new target. Optional rewriteTarget(SyntacticElementTarget target); @@ -8820,12 +8823,68 @@ static Expr *wrapAsyncLetInitializer( return resultInit; } +static Pattern *rewriteExprPattern(const SyntacticElementTarget &matchTarget, + Type patternTy, + RewriteTargetFn rewriteTarget) { + auto *EP = matchTarget.getExprPattern(); + + // See if we can simplify to another kind of pattern. + if (auto simplified = TypeChecker::trySimplifyExprPattern(EP, patternTy)) + return simplified.get(); + + auto resultTarget = rewriteTarget(matchTarget); + if (!resultTarget) + return nullptr; + + EP->setMatchExpr(resultTarget->getAsExpr()); + EP->getMatchVar()->setInterfaceType(patternTy->mapTypeOutOfContext()); + EP->setType(patternTy); + return EP; +} + +/// Attempt to rewrite either an ExprPattern, or a pattern that was solved as +/// an ExprPattern, e.g an EnumElementPattern that could not refer to an enum +/// case. +static Optional +tryRewriteExprPattern(Pattern *P, Solution &solution, Type patternTy, + RewriteTargetFn rewriteTarget) { + // See if we have a match expression target. + auto matchTarget = solution.getTargetFor(P); + if (!matchTarget) + return None; + + return rewriteExprPattern(*matchTarget, patternTy, rewriteTarget); +} + +NullablePtr ExprWalker::rewritePattern(Pattern *pattern, + DeclContext *DC) { + auto &solution = Rewriter.solution; + + // Figure out the pattern type. + auto patternTy = solution.getResolvedType(pattern); + patternTy = patternTy->reconstituteSugar(/*recursive=*/false); + + // Coerce the pattern to its appropriate type. + TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); + patternOptions |= TypeResolutionFlags::OverrideType; + + auto tryRewritePattern = [&](Pattern *EP, Type ty) { + return ::tryRewriteExprPattern( + EP, solution, ty, [&](auto target) { return rewriteTarget(target); }); + }; + + auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); + return TypeChecker::coercePatternToType(contextualPattern, patternTy, + patternOptions, tryRewritePattern); +} + /// Apply the given solution to the initialization target. /// /// \returns the resulting initialization expression. static Optional applySolutionToInitialization(Solution &solution, SyntacticElementTarget target, - Expr *initializer) { + Expr *initializer, + RewriteTargetFn rewriteTarget) { auto wrappedVar = target.getInitializationWrappedVar(); Type initType; if (wrappedVar) { @@ -8890,10 +8949,14 @@ applySolutionToInitialization(Solution &solution, SyntacticElementTarget target, finalPatternType = finalPatternType->reconstituteSugar(/*recursive =*/false); + auto tryRewritePattern = [&](Pattern *EP, Type ty) { + return ::tryRewriteExprPattern(EP, solution, ty, rewriteTarget); + }; + // Apply the solution to the pattern as well. auto contextualPattern = target.getContextualPattern(); if (auto coercedPattern = TypeChecker::coercePatternToType( - contextualPattern, finalPatternType, options)) { + contextualPattern, finalPatternType, options, tryRewritePattern)) { resultTarget.setPattern(coercedPattern); } else { return None; @@ -9040,10 +9103,15 @@ static Optional applySolutionToForEachStmt( TypeResolutionOptions options(TypeResolverContext::ForEachStmt); options |= TypeResolutionFlags::OverrideType; + auto tryRewritePattern = [&](Pattern *EP, Type ty) { + return ::tryRewriteExprPattern(EP, solution, ty, rewriteTarget); + }; + // Apply the solution to the pattern as well. auto contextualPattern = target.getContextualPattern(); auto coercedPattern = TypeChecker::coercePatternToType( - contextualPattern, forEachStmtInfo.initType, options); + contextualPattern, forEachStmtInfo.initType, options, + tryRewritePattern); if (!coercedPattern) return None; @@ -9131,7 +9199,8 @@ ExprWalker::rewriteTarget(SyntacticElementTarget target) { switch (target.getExprContextualTypePurpose()) { case CTP_Initialization: { auto initResultTarget = applySolutionToInitialization( - solution, target, rewrittenExpr); + solution, target, rewrittenExpr, + [&](auto target) { return rewriteTarget(target); }); if (!initResultTarget) return None; @@ -9222,47 +9291,11 @@ ExprWalker::rewriteTarget(SyntacticElementTarget target) { ConstraintSystem &cs = solution.getConstraintSystem(); auto info = *cs.getCaseLabelItemInfo(*caseLabelItem); - // Figure out the pattern type. - Type patternType = solution.simplifyType(solution.getType(info.pattern)); - patternType = patternType->reconstituteSugar(/*recursive=*/false); - - // Check whether this enum element is resolved via ~= application. - if (auto *enumElement = dyn_cast(info.pattern)) { - if (auto target = cs.getTargetFor(enumElement)) { - auto *EP = target->getExprPattern(); - auto enumType = solution.getResolvedType(EP); - - auto *matchCall = target->getAsExpr(); - - auto *result = matchCall->walk(*this); - if (!result) - return None; - - { - auto *matchVar = EP->getMatchVar(); - matchVar->setInterfaceType(enumType->mapTypeOutOfContext()); - } - - EP->setMatchExpr(result); - EP->setType(enumType); - - (*caseLabelItem)->setPattern(EP, /*resolved=*/true); - return target; - } - } - - // Coerce the pattern to its appropriate type. - TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); - patternOptions |= TypeResolutionFlags::OverrideType; - auto contextualPattern = - ContextualPattern::forRawPattern(info.pattern, - target.getDeclContext()); - if (auto coercedPattern = TypeChecker::coercePatternToType( - contextualPattern, patternType, patternOptions)) { - (*caseLabelItem)->setPattern(coercedPattern, /*resolved=*/true); - } else { + auto pattern = rewritePattern(info.pattern, target.getDeclContext()); + if (!pattern) return None; - } + + (*caseLabelItem)->setPattern(pattern.get(), /*resolved=*/true); // If there is a guard expression, coerce that. if (auto *guardExpr = info.guardExpr) { @@ -9330,8 +9363,13 @@ ExprWalker::rewriteTarget(SyntacticElementTarget target) { options |= TypeResolutionFlags::OverrideType; } + auto tryRewritePattern = [&](Pattern *EP, Type ty) { + return ::tryRewriteExprPattern( + EP, solution, ty, [&](auto target) { return rewriteTarget(target); }); + }; + if (auto coercedPattern = TypeChecker::coercePatternToType( - contextualPattern, patternType, options)) { + contextualPattern, patternType, options, tryRewritePattern)) { auto resultTarget = target; resultTarget.setPattern(coercedPattern); return resultTarget; diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 9b6012ec123db..f1cf10a1507fc 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -8589,7 +8589,7 @@ bool InvalidWeakAttributeUse::diagnoseAsError() { return false; auto *var = pattern->getDecl(); - auto varType = OptionalType::get(getType(var)); + auto varType = getType(var); auto diagnostic = emitDiagnosticAt(var, diag::invalid_ownership_not_optional, diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 382374a110dee..800740c995531 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2378,37 +2378,21 @@ namespace { /// \param locator The locator to use for generated constraints and /// type variables. /// - /// \param externalPatternType The type imposed by the enclosing pattern, - /// if any. This will be non-null in cases where there is, e.g., a - /// pattern such as "is SubClass". - /// /// \param bindPatternVarsOneWay When true, generate fresh type variables /// for the types of each variable declared within the pattern, along /// with a one-way constraint binding that to the type to which the /// variable will be ascribed or inferred. Type getTypeForPattern( Pattern *pattern, ConstraintLocatorBuilder locator, - Type externalPatternType, bool bindPatternVarsOneWay, PatternBindingDecl *patternBinding = nullptr, unsigned patternBindingIndex = 0) { - // If there's no pattern, then we have an unknown subpattern. Create a - // type variable. - if (!pattern) { - return CS.createTypeVariable(CS.getConstraintLocator(locator), - TVO_CanBindToNoEscape); - } + assert(pattern); // Local function that must be called for each "return" throughout this // function, to set the type of the pattern. auto setType = [&](Type type) { CS.setType(pattern, type); - if (auto PE = dyn_cast(pattern)) { - // Set the type of the pattern's sub-expression as well, so code - // completion can retrieve the expression's type in case it is a code - // completion token. - CS.setType(PE->getSubExpr(), type); - } return type; }; @@ -2416,33 +2400,18 @@ namespace { case PatternKind::Paren: { auto *paren = cast(pattern); - // Parentheses don't affect the canonical type, but record them as - // type sugar. - if (externalPatternType && - isa(externalPatternType.getPointer())) { - externalPatternType = cast(externalPatternType.getPointer()) - ->getUnderlyingType(); - } - auto *subPattern = paren->getSubPattern(); auto underlyingType = getTypeForPattern( subPattern, locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - externalPatternType, bindPatternVarsOneWay); - - if (!underlyingType) - return Type(); + bindPatternVarsOneWay); return setType(ParenType::get(CS.getASTContext(), underlyingType)); } case PatternKind::Binding: { auto *subPattern = cast(pattern)->getSubPattern(); - auto type = getTypeForPattern(subPattern, locator, externalPatternType, + auto type = getTypeForPattern(subPattern, locator, bindPatternVarsOneWay); - - if (!type) - return Type(); - // Var doesn't affect the type. return setType(type); } @@ -2463,9 +2432,7 @@ namespace { }; // Always prefer a contextual type when it's available. - if (externalPatternType) { - type = externalPatternType; - } else if (auto *initializer = getInitializerExpr()) { + if (auto *initializer = getInitializerExpr()) { // For initialization always assume a type of initializer. type = CS.getType(initializer)->getRValueType(); } else { @@ -2542,18 +2509,13 @@ namespace { // diagnostic in the middle of the solver path. CS.addConstraint(ConstraintKind::OneWayEqual, oneWayVarType, - externalPatternType ? externalPatternType : varType, - locator); + varType, locator); } // Ascribe a type to the declaration so it's always available to // constraint system. if (oneWayVarType) { CS.setType(var, oneWayVarType); - } else if (externalPatternType) { - // If there is an externally imposed type, that's what the - // declaration is going to be bound to. - CS.setType(var, externalPatternType); } else { // Otherwise, let's use the type of the pattern. The type // of the declaration has to be r-value, so let's add an @@ -2625,9 +2587,6 @@ namespace { Type type = TypeChecker::typeCheckPattern(contextualPattern); - if (!type) - return Type(); - // Look through reference storage types. type = type->getReferenceStorageReferent(); @@ -2642,13 +2601,13 @@ namespace { Type subPatternType = getTypeForPattern( subPattern, locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - openedType, bindPatternVarsOneWay); - - if (!subPatternType) - return Type(); + bindPatternVarsOneWay); + // NOTE: The order here is important! Pattern matching equality is + // not symmetric (we need to fix that either by using a different + // constraint, or actually making it symmetric). CS.addConstraint( - ConstraintKind::Conversion, subPatternType, openedType, + ConstraintKind::Equal, openedType, subPatternType, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); // FIXME [OPAQUE SUPPORT]: the distinction between where we want opaque @@ -2662,41 +2621,16 @@ namespace { case PatternKind::Tuple: { auto tuplePat = cast(pattern); - // If there's an externally-imposed type, decompose it into element - // types so long as we have the right number of such types. - SmallVector externalEltTypes; - if (externalPatternType) { - decomposeTuple(externalPatternType, externalEltTypes); - - // If we have the wrong number of elements, we may not be able to - // provide more specific types. - if (tuplePat->getNumElements() != externalEltTypes.size()) { - externalEltTypes.clear(); - - // Implicit tupling. - if (tuplePat->getNumElements() == 1) { - externalEltTypes.push_back( - AnyFunctionType::Param(externalPatternType)); - } - } - } - SmallVector tupleTypeElts; tupleTypeElts.reserve(tuplePat->getNumElements()); for (unsigned i = 0, e = tuplePat->getNumElements(); i != e; ++i) { auto &tupleElt = tuplePat->getElement(i); - Type externalEltType; - if (!externalEltTypes.empty()) - externalEltType = externalEltTypes[i].getPlainType(); auto *eltPattern = tupleElt.getPattern(); Type eltTy = getTypeForPattern( eltPattern, locator.withPathElement(LocatorPathElt::PatternMatch(eltPattern)), - externalEltType, bindPatternVarsOneWay); - - if (!eltTy) - return Type(); + bindPatternVarsOneWay); tupleTypeElts.push_back(TupleTypeElt(eltTy, tupleElt.getLabel())); } @@ -2705,28 +2639,12 @@ namespace { } case PatternKind::OptionalSome: { - // Remove an optional from the object type. - if (externalPatternType) { - Type objVar = CS.createTypeVariable( - CS.getConstraintLocator( - locator.withPathElement(ConstraintLocator::OptionalPayload)), - TVO_CanBindToNoEscape); - CS.addConstraint( - ConstraintKind::OptionalObject, externalPatternType, objVar, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); - - externalPatternType = objVar; - } - auto *subPattern = cast(pattern)->getSubPattern(); // The subpattern must have optional type. Type subPatternType = getTypeForPattern( subPattern, locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - externalPatternType, bindPatternVarsOneWay); - - if (!subPatternType) - return Type(); + bindPatternVarsOneWay); return setType(OptionalType::get(subPatternType)); } @@ -2737,22 +2655,6 @@ namespace { const Type castType = resolveTypeReferenceInExpression( isPattern->getCastTypeRepr(), TypeResolverContext::InExpression, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); - if (!castType) return Type(); - - auto *subPattern = isPattern->getSubPattern(); - Type subPatternType = getTypeForPattern( - subPattern, - locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - castType, bindPatternVarsOneWay); - - if (!subPatternType) - return Type(); - - // Make sure we can cast from the subpattern type to the type we're - // checking; if it's impossible, fail. - CS.addConstraint( - ConstraintKind::CheckedCast, subPatternType, castType, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); // Allow `is` pattern to infer type from context which is then going // to be propaged down to its sub-pattern via conversion. This enables @@ -2761,9 +2663,26 @@ namespace { // inferred from enclosing context. auto isType = CS.createTypeVariable(CS.getConstraintLocator(pattern), TVO_CanBindToNoEscape); + + // Make sure we can cast from the subpattern type to the type we're + // checking; if it's impossible, fail. CS.addConstraint( - ConstraintKind::Conversion, subPatternType, isType, + ConstraintKind::CheckedCast, isType, castType, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + + if (auto *subPattern = isPattern->getSubPattern()) { + auto subPatternType = getTypeForPattern( + subPattern, + locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), + bindPatternVarsOneWay); + + // NOTE: The order here is important! Pattern matching equality is + // not symmetric (we need to fix that either by using a different + // constraint, or actually making it symmetric). + CS.addConstraint( + ConstraintKind::Equal, castType, subPatternType, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + } return setType(isType); } @@ -2822,9 +2741,6 @@ namespace { TypeResolverContext::InExpression, patternMatchLoc); }(); - if (!parentType) - return Type(); - // Perform member lookup into the parent's metatype. Type parentMetaType = MetatypeType::get(parentType); CS.addValueMemberConstraint( @@ -2840,18 +2756,6 @@ namespace { locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); baseType = parentType; - // Perform member lookup into the external pattern metatype. e.g. - // `case let .test(tuple) as Test`. - } else if (externalPatternType) { - Type externalMetaType = MetatypeType::get(externalPatternType); - - CS.addValueMemberConstraint( - externalMetaType, enumPattern->getName(), memberType, CurDC, - functionRefKind, {}, - CS.getConstraintLocator(locator, - LocatorPathElt::PatternMatch(pattern))); - - baseType = externalPatternType; } else { // Use the pattern type for member lookup. CS.addUnresolvedValueMemberConstraint( @@ -2869,10 +2773,7 @@ namespace { Type subPatternType = getTypeForPattern( subPattern, locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - Type(), bindPatternVarsOneWay); - - if (!subPatternType) - return Type(); + bindPatternVarsOneWay); SmallVector params; decomposeTuple(subPatternType, params); @@ -2890,7 +2791,13 @@ namespace { // FIXME: Verify ExtInfo state is correct, not working by accident. FunctionType::ExtInfo info; Type functionType = FunctionType::get(params, outputType, info); - // TODO: Convert to FunctionInput/FunctionResult constraints. + + // TODO: Convert to own constraint? Note that ApplicableFn isn't quite + // right, as pattern matching has data flowing *into* the apply result + // and call arguments, no the other way around. + // NOTE: The order here is important! Pattern matching equality is + // not symmetric (we need to fix that either by using a different + // constraint, or actually making it symmetric). CS.addConstraint( ConstraintKind::Equal, functionType, memberType, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); @@ -2903,15 +2810,12 @@ namespace { return setType(patternType); } - // Refutable patterns occur when checking the PatternBindingDecls in an - // if/let or while/let condition. They always require an initial value, - // so they always allow unspecified types. - case PatternKind::Expr: - // TODO: we could try harder here, e.g. for enum elements to provide the - // enum type. - return setType( - CS.createTypeVariable( - CS.getConstraintLocator(locator), TVO_CanBindToNoEscape)); + case PatternKind::Expr: { + // We generate constraints for ExprPatterns in a separate pass. For + // now, just create a type variable. + return setType(CS.createTypeVariable(CS.getConstraintLocator(locator), + TVO_CanBindToNoEscape)); + } } llvm_unreachable("Unhandled pattern kind"); @@ -4288,16 +4192,18 @@ bool ConstraintSystem::generateWrappedPropertyTypeConstraints( static bool generateInitPatternConstraints(ConstraintSystem &cs, SyntacticElementTarget target, Expr *initializer) { - auto pattern = target.getInitializationPattern(); auto locator = cs.getConstraintLocator( initializer, LocatorPathElt::ContextualType(CTP_Initialization)); - Type patternType = cs.generateConstraints( - pattern, locator, target.shouldBindPatternVarsOneWay(), - target.getInitializationPatternBindingDecl(), - target.getInitializationPatternBindingIndex()); - if (!patternType) - return true; + Type patternType; + if (auto pattern = target.getInitializationPattern()) { + patternType = cs.generateConstraints( + pattern, locator, target.shouldBindPatternVarsOneWay(), + target.getInitializationPatternBindingDecl(), + target.getInitializationPatternBindingIndex()); + } else { + patternType = cs.createTypeVariable(locator, TVO_CanBindToNoEscape); + } if (auto wrappedVar = target.getInitializationWrappedVar()) return cs.generateWrappedPropertyTypeConstraints( @@ -4712,8 +4618,20 @@ Type ConstraintSystem::generateConstraints( bool bindPatternVarsOneWay, PatternBindingDecl *patternBinding, unsigned patternIndex) { ConstraintGenerator cg(*this, nullptr); - return cg.getTypeForPattern(pattern, locator, Type(), bindPatternVarsOneWay, - patternBinding, patternIndex); + auto ty = cg.getTypeForPattern(pattern, locator, bindPatternVarsOneWay, + patternBinding, patternIndex); + assert(ty); + + // Gather the ExprPatterns, and form a conjunction for their expressions. + SmallVector exprPatterns; + pattern->forEachNode([&](Pattern *P) { + if (auto *EP = dyn_cast(P)) + exprPatterns.push_back(EP); + }); + if (!exprPatterns.empty()) + generateConstraints(exprPatterns, getConstraintLocator(pattern)); + + return ty; } bool ConstraintSystem::generateConstraints(StmtCondition condition, diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index d55518f713f8c..6998e42f479e3 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2125,7 +2125,9 @@ class TupleMatcher { const auto &elt1 = tuple1->getElement(i); const auto &elt2 = tuple2->getElement(i); - if (elt1.hasName() && elt1.getName() != elt2.getName()) + // FIXME: The fact that this isn't symmetric is wrong since this logic + // is called for bind and equal constraints... + if (elt2.hasName() && elt1.getName() != elt2.getName()) return true; pairs.emplace_back(elt1.getType(), elt2.getType(), i, i); @@ -3502,21 +3504,12 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, // FIXME: We should check value ownership too, but it's not completely // trivial because of inout-to-pointer conversions. - // For equality contravariance doesn't matter, but let's make sure - // that types are matched in original order because that is important - // when function types are equated as part of pattern matching. - auto paramType1 = kind == ConstraintKind::Equal ? func1Param.getOldType() - : func2Param.getOldType(); - - auto paramType2 = kind == ConstraintKind::Equal ? func2Param.getOldType() - : func1Param.getOldType(); - - // Compare the parameter types. - auto result = matchTypes(paramType1, paramType2, subKind, subflags, - (func1Params.size() == 1 - ? argumentLocator - : argumentLocator.withPathElement( - LocatorPathElt::TupleElement(i)))); + // Compare the parameter types, taking contravariance into account. + auto result = matchTypes( + func2Param.getOldType(), func1Param.getOldType(), subKind, subflags, + (func1Params.size() == 1 ? argumentLocator + : argumentLocator.withPathElement( + LocatorPathElt::TupleElement(i)))); if (result.isFailure()) return result; } @@ -6374,7 +6367,7 @@ bool ConstraintSystem::repairFailures( if (auto *OA = var->getAttrs().getAttribute()) ROK = OA->get(); - if (!rhs->getOptionalObjectType() && + if (!lhs->getOptionalObjectType() && optionalityOf(ROK) == ReferenceOwnershipOptionality::Required) { conversionsOrFixes.push_back( AllowNonOptionalWeak::create(*this, getConstraintLocator(NP))); @@ -9964,17 +9957,13 @@ static bool inferEnumMemberThroughTildeEqualsOperator( if (!pattern->hasUnresolvedOriginalExpr()) return true; - auto *DC = pattern->getDeclContext(); auto &ctx = cs.getASTContext(); // Retrieve a corresponding ExprPattern which we can solve with ~=. auto *EP = llvm::cantFail(ctx.evaluator(EnumElementExprPatternRequest{pattern})); - // result of ~= operator is always a `Bool`. - auto *matchCall = EP->getMatchExpr(); - auto target = SyntacticElementTarget::forExprPattern( - matchCall, DC, EP, ctx.getBoolDecl()->getDeclaredInterfaceType()); + auto target = SyntacticElementTarget::forExprPattern(EP); DiagnosticTransaction diagnostics(ctx.Diags); { @@ -14317,8 +14306,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( if (recordFix(fix)) return SolutionKind::Error; - (void)matchTypes(type1, OptionalType::get(type2), - ConstraintKind::Conversion, + // NOTE: The order here is important! Pattern matching equality is + // not symmetric (we need to fix that either by using a different + // constraint, or actually making it symmetric). + (void)matchTypes(OptionalType::get(type1), type2, ConstraintKind::Equal, TypeMatchFlags::TMF_ApplyingFix, locator); return SolutionKind::Solved; diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index fc5552e11fe2b..1c8fa07ed8b50 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -187,6 +187,7 @@ Solution ConstraintSystem::finalize() { solution.targets = targets; solution.caseLabelItems = caseLabelItems; + solution.exprPatterns = exprPatterns; solution.isolatedParams.append(isolatedParams.begin(), isolatedParams.end()); solution.preconcurrencyClosures.append(preconcurrencyClosures.begin(), preconcurrencyClosures.end()); @@ -310,6 +311,9 @@ void ConstraintSystem::applySolution(const Solution &solution) { isolatedParams.insert(param); } + for (auto &pair : solution.exprPatterns) + exprPatterns.insert(pair); + for (auto closure : solution.preconcurrencyClosures) { preconcurrencyClosures.insert(closure); } @@ -603,6 +607,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numContextualTypes = cs.contextualTypes.size(); numTargets = cs.targets.size(); numCaseLabelItems = cs.caseLabelItems.size(); + numExprPatterns = cs.exprPatterns.size(); numIsolatedParams = cs.isolatedParams.size(); numPreconcurrencyClosures = cs.preconcurrencyClosures.size(); numImplicitValueConversions = cs.ImplicitValueConversions.size(); @@ -716,6 +721,9 @@ ConstraintSystem::SolverScope::~SolverScope() { // Remove any case label item infos. truncate(cs.caseLabelItems, numCaseLabelItems); + // Remove any ExprPattern mappings. + truncate(cs.exprPatterns, numExprPatterns); + // Remove any isolated parameters. truncate(cs.isolatedParams, numIsolatedParams); diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 17ebeeebd333a..2a38aacc1e011 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -350,6 +350,18 @@ static void createConjunction(ConstraintSystem &cs, isIsolated = true; } + if (locator->isLastElement()) { + // Collect the ExprPattern type variables. + auto *P = castToPattern(locator->getAnchor()); + P->forEachNode([&](Pattern *subPattern) { + if (auto *EP = dyn_cast(subPattern)) + referencedVars.push_back(cs.getType(EP)->castTo()); + }); + + // We solve ExprPatterns in isolation from the rest of the pattern. + isIsolated = true; + } + UnresolvedVarCollector paramCollector(cs); for (const auto &entry : elements) { @@ -401,7 +413,7 @@ ElementInfo makeJoinElement(ConstraintSystem &cs, TypeJoinExpr *join, struct SyntacticElementContext : public llvm::PointerUnion { + SingleValueStmtExpr *, ExprPattern *> { // Inherit the constructors from PointerUnion. using PointerUnion::PointerUnion; @@ -432,6 +444,10 @@ struct SyntacticElementContext return context; } + static SyntacticElementContext forExprPattern(ExprPattern *EP) { + return SyntacticElementContext{EP}; + } + DeclContext *getAsDeclContext() const { if (auto *fn = this->dyn_cast()) { return fn; @@ -439,6 +455,8 @@ struct SyntacticElementContext return closure; } else if (auto *SVE = dyn_cast()) { return SVE->getDeclContext(); + } else if (auto *EP = dyn_cast()) { + return EP->getDeclContext(); } else { llvm_unreachable("unsupported kind"); } @@ -510,7 +528,32 @@ class SyntacticElementConstraintGenerator ConstraintLocator *locator) : cs(cs), context(context), locator(locator) {} - void visitPattern(Pattern *pattern, ContextualTypeInfo context) { + void visitExprPattern(ExprPattern *EP) { + auto target = SyntacticElementTarget::forExprPattern(EP); + + if (cs.preCheckTarget(target, /*replaceInvalidRefWithErrors=*/true, + /*leaveClosureBodyUnchecked=*/false)) { + hadError = true; + return; + } + cs.setType(EP->getMatchVar(), cs.getType(EP)); + + if (cs.generateConstraints(target, FreeTypeVariableBinding::Disallow)) { + hadError = true; + return; + } + cs.setTargetFor(EP, target); + cs.setExprPatternFor(EP->getSubExpr(), EP); + } + + void visitPattern(Pattern *pattern, ContextualTypeInfo contextInfo) { + if (context.is()) { + // This is for an ExprPattern conjunction, go ahead and generate + // constraints for the match expression. + visitExprPattern(cast(pattern)); + return; + } + auto parentElement = locator->getLastElementAs(); @@ -526,7 +569,7 @@ class SyntacticElementConstraintGenerator } if (isa(stmt)) { - visitCaseItemPattern(pattern, context); + visitCaseItemPattern(pattern, contextInfo); return; } } @@ -1391,6 +1434,20 @@ bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) { return generator.hadError; } +void ConstraintSystem::generateConstraints(ArrayRef exprPatterns, + ConstraintLocatorBuilder locator) { + // Form a conjunction of ExprPattern elements. + SmallVector elements; + for (auto *EP : exprPatterns) { + auto *loc = getConstraintLocator(EP); + ContextualTypeInfo context(getType(EP), CTP_ExprPattern); + elements.push_back(makeElement(EP, loc, context)); + } + auto *loc = + getConstraintLocator(locator, LocatorPathElt::ExprPatternConjunction()); + createConjunction(*this, elements, loc); +} + bool ConstraintSystem::isInResultBuilderContext(ClosureExpr *closure) const { if (!closure->hasSingleExpressionBody()) { auto *DC = closure->getParent(); @@ -1442,6 +1499,8 @@ ConstraintSystem::simplifySyntacticElementConstraint( context = SyntacticElementContext::forFunction(fn); } else if (auto *SVE = getAsExpr(anchor)) { context = SyntacticElementContext::forSingleValueStmtExpr(SVE); + } else if (auto *EP = getAsPattern(anchor)) { + context = SyntacticElementContext::forExprPattern(EP); } else { return SolutionKind::Error; } diff --git a/lib/Sema/CompletionContextFinder.cpp b/lib/Sema/CompletionContextFinder.cpp index f4ef0f7a9336f..9279a038d160a 100644 --- a/lib/Sema/CompletionContextFinder.cpp +++ b/lib/Sema/CompletionContextFinder.cpp @@ -12,10 +12,19 @@ #include "swift/Sema/CompletionContextFinder.h" #include "swift/Parse/Lexer.h" +#include "swift/Sema/SyntacticElementTarget.h" using namespace swift; +using namespace constraints; using Fallback = CompletionContextFinder::Fallback; +CompletionContextFinder::CompletionContextFinder( + SyntacticElementTarget target, DeclContext *DC) + : InitialExpr(target.getAsExpr()), InitialDC(DC) { + assert(DC); + target.walk(*this); +} + ASTWalker::PreWalkResult CompletionContextFinder::walkToExprPre(Expr *E) { if (auto *closure = dyn_cast(E)) { diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 8054d4534e1ba..d13479f9eb5f3 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -104,6 +104,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::NamedPatternDecl: case ConstraintLocator::SingleValueStmtBranch: case ConstraintLocator::AnyPatternDecl: + case ConstraintLocator::ExprPatternConjunction: case ConstraintLocator::GlobalActorType: return 0; @@ -492,6 +493,11 @@ void LocatorPathElt::dump(raw_ostream &out) const { break; } + case ConstraintLocator::ExprPatternConjunction: { + out << "expr pattern conjunction"; + break; + } + case ConstraintLocator::GlobalActorType: { out << "global actor type"; break; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index ccbea39cb6956..8d6046a73b7d6 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -5666,6 +5666,7 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::Witness: case ConstraintLocator::WrappedValue: case ConstraintLocator::OptionalPayload: + case ConstraintLocator::ExprPatternConjunction: case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: break; } diff --git a/lib/Sema/SyntacticElementTarget.cpp b/lib/Sema/SyntacticElementTarget.cpp index a35dbc0a63d9c..a4ddbba2e0e2e 100644 --- a/lib/Sema/SyntacticElementTarget.cpp +++ b/lib/Sema/SyntacticElementTarget.cpp @@ -202,6 +202,18 @@ SyntacticElementTarget SyntacticElementTarget::forPropertyWrapperInitializer( return target; } +SyntacticElementTarget +SyntacticElementTarget::forExprPattern(ExprPattern *pattern) { + auto *DC = pattern->getDeclContext(); + auto &ctx = DC->getASTContext(); + + // Result of ~= operator is always a `Bool`. + SyntacticElementTarget target(pattern->getMatchExpr(), DC, CTP_ExprPattern, + ctx.getBoolType(), /*isDiscarded*/ false); + target.setPattern(pattern); + return target; +} + ContextualPattern SyntacticElementTarget::getContextualPattern() const { if (kind == Kind::uninitializedVar) { assert(patternBinding); diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index e93d22a6f06ca..58acb0c1e1965 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -574,15 +574,12 @@ bool TypeChecker::typeCheckForCodeCompletion( return false; } - auto node = target.getAsASTNode(); - if (!node) - return false; - - if (auto *expr = getAsExpr(node)) { - node = expr->walk(SanitizeExpr(Context)); + if (getAsExpr(target.getAsASTNode())) { + SanitizeExpr sanitizer(Context); + target = *target.walk(sanitizer); } - CompletionContextFinder contextAnalyzer(node, DC); + CompletionContextFinder contextAnalyzer(target, DC); // If there was no completion expr (e.g. if the code completion location was // among tokens that were skipped over during parser error recovery) bail. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 552d76b59269b..680e55f17764b 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -919,7 +919,7 @@ bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { return !resultTy; } -/// Find the '~=` operator that can compare an expression inside a pattern to a +/// Find the `~=` operator that can compare an expression inside a pattern to a /// value of a given type. bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, Type rhsType) { @@ -930,13 +930,8 @@ bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, EP->getMatchVar()->setInterfaceType(rhsType->mapTypeOutOfContext()); - // Result of `~=` should always be a boolean. - auto *matchCall = EP->getMatchExpr(); - auto contextualTy = Context.getBoolDecl()->getDeclaredInterfaceType(); - auto target = - SyntacticElementTarget::forExprPattern(matchCall, DC, EP, contextualTy); - // Check the expression as a condition. + auto target = SyntacticElementTarget::forExprPattern(EP); auto result = typeCheckExpression(target); if (!result) return true; diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 9b15982e35b66..ec30f50f2305e 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1034,15 +1034,61 @@ void repairTupleOrAssociatedValuePatternIfApplicable( enumCase->getName()); } +NullablePtr TypeChecker::trySimplifyExprPattern(ExprPattern *EP, + Type patternTy) { + auto *subExpr = EP->getSubExpr(); + auto &ctx = EP->getDeclContext()->getASTContext(); + + if (patternTy->isBool()) { + // The type is Bool. + // Check if the pattern is a Bool literal + auto *semanticSubExpr = subExpr->getSemanticsProvidingExpr(); + if (auto *BLE = dyn_cast(semanticSubExpr)) { + auto *BP = new (ctx) BoolPattern(BLE->getLoc(), BLE->getValue()); + BP->setType(patternTy); + return BP; + } + } + + // case nil is equivalent to .none when switching on Optionals. + if (auto *NLE = dyn_cast(EP->getSubExpr())) { + if (patternTy->getOptionalObjectType()) { + auto *NoneEnumElement = ctx.getOptionalNoneDecl(); + auto *BaseTE = TypeExpr::createImplicit(patternTy, ctx); + auto *EEP = new (ctx) + EnumElementPattern(BaseTE, NLE->getLoc(), DeclNameLoc(NLE->getLoc()), + NoneEnumElement->createNameRef(), NoneEnumElement, + nullptr, EP->getDeclContext()); + EEP->setType(patternTy); + return EEP; + } else { + // ...but for non-optional types it can never match! Diagnose it. + ctx.Diags + .diagnose(NLE->getLoc(), diag::value_type_comparison_with_nil_illegal, + patternTy) + .warnUntilSwiftVersion(6); + + if (ctx.isSwiftVersionAtLeast(6)) + return nullptr; + } + } + return nullptr; +} + /// Perform top-down type coercion on the given pattern. -Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, - Type type, - TypeResolutionOptions options) { +Pattern *TypeChecker::coercePatternToType( + ContextualPattern pattern, Type type, TypeResolutionOptions options, + llvm::function_ref(Pattern *, Type)> + tryRewritePattern) { auto P = pattern.getPattern(); auto dc = pattern.getDeclContext(); auto &Context = dc->getASTContext(); auto &diags = Context.Diags; + // See if we can rewrite this using the constraint system. + if (auto result = tryRewritePattern(P, type)) + return *result; + options = applyContextualPatternOptions(options, pattern); auto subOptions = options; subOptions.setContext(None); @@ -1061,8 +1107,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, if (tupleType->getNumElements() == 1) { auto element = tupleType->getElement(0); sub = coercePatternToType( - pattern.forSubPattern(sub, /*retainTopLevel=*/true), element.getType(), - subOptions); + pattern.forSubPattern(sub, /*retainTopLevel=*/true), + element.getType(), subOptions, tryRewritePattern); if (!sub) return nullptr; TuplePatternElt elt(element.getName(), SourceLoc(), sub); @@ -1077,7 +1123,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, } sub = coercePatternToType( - pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions, + tryRewritePattern); if (!sub) return nullptr; @@ -1090,7 +1137,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, Pattern *sub = VP->getSubPattern(); sub = coercePatternToType( - pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions); + pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, subOptions, + tryRewritePattern); if (!sub) return nullptr; VP->setSubPattern(sub); @@ -1123,7 +1171,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, Pattern *sub = TP->getSubPattern(); sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, - subOptions | TypeResolutionFlags::FromNonInferredPattern); + subOptions | TypeResolutionFlags::FromNonInferredPattern, + tryRewritePattern); if (!sub) return nullptr; @@ -1212,9 +1261,9 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, auto decayToParen = [&]() -> Pattern * { assert(canDecayToParen); Pattern *sub = TP->getElement(0).getPattern(); - sub = TypeChecker::coercePatternToType( + sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), type, - subOptions); + subOptions, tryRewritePattern); if (!sub) return nullptr; @@ -1271,7 +1320,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, auto sub = coercePatternToType( pattern.forSubPattern(elt.getPattern(), /*retainTopLevel=*/false), - CoercionType, subOptions); + CoercionType, subOptions, tryRewritePattern); if (!sub) return nullptr; @@ -1291,37 +1340,9 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, assert(cast(P)->isResolved() && "coercing unresolved expr pattern!"); auto *EP = cast(P); - if (type->isBool()) { - // The type is Bool. - // Check if the pattern is a Bool literal - if (auto *BLE = dyn_cast( - EP->getSubExpr()->getSemanticsProvidingExpr())) { - P = new (Context) BoolPattern(BLE->getLoc(), BLE->getValue()); - P->setType(type); - return P; - } - } - // case nil is equivalent to .none when switching on Optionals. - if (auto *NLE = dyn_cast(EP->getSubExpr())) { - if (type->getOptionalObjectType()) { - auto *NoneEnumElement = Context.getOptionalNoneDecl(); - auto *BaseTE = TypeExpr::createImplicit(type, Context); - P = new (Context) EnumElementPattern( - BaseTE, NLE->getLoc(), DeclNameLoc(NLE->getLoc()), - NoneEnumElement->createNameRef(), NoneEnumElement, nullptr, dc); - return TypeChecker::coercePatternToType( - pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options); - } else { - // ...but for non-optional types it can never match! Diagnose it. - diags.diagnose(NLE->getLoc(), - diag::value_type_comparison_with_nil_illegal, type) - .warnUntilSwiftVersion(6); - - if (type->getASTContext().isSwiftVersionAtLeast(6)) - return nullptr; - } - } + if (auto P = trySimplifyExprPattern(EP, type)) + return P.get(); if (TypeChecker::typeCheckExprPattern(EP, dc, type)) return nullptr; @@ -1370,7 +1391,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, P = sub; return coercePatternToType( - pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options); + pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options, + tryRewritePattern); } CheckedCastKind castKind = TypeChecker::typeCheckCheckedCast( @@ -1419,7 +1441,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), IP->getCastType(), - subOptions | TypeResolutionFlags::FromNonInferredPattern); + subOptions | TypeResolutionFlags::FromNonInferredPattern, + tryRewritePattern); if (!sub) return nullptr; @@ -1457,7 +1480,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, EEP->getEndLoc()); return coercePatternToType( pattern.forSubPattern(P, /*retainTopLevel=*/true), type, - options); + options, tryRewritePattern); } else { diags.diagnose(EEP->getLoc(), diag::enum_element_pattern_member_not_found, @@ -1472,7 +1495,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, Context, EEP->getUnresolvedOriginalExpr(), dc); return coercePatternToType( pattern.forSubPattern(P, /*retainTopLevel=*/true), type, - options); + options, tryRewritePattern); } } } @@ -1595,7 +1618,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, - newSubOptions); + newSubOptions, tryRewritePattern); if (!sub) return nullptr; @@ -1630,7 +1653,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, - newSubOptions); + newSubOptions, tryRewritePattern); if (!sub) return nullptr; EEP->setSubPattern(sub); @@ -1674,7 +1697,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, newSubOptions |= TypeResolutionFlags::FromNonInferredPattern; sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, - newSubOptions); + newSubOptions, tryRewritePattern); if (!sub) return nullptr; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 91418e8f31868..03006e4f29d21 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -723,15 +723,26 @@ Pattern *resolvePattern(Pattern *P, DeclContext *dc, bool isStmtCondition); /// unbound generic types. Type typeCheckPattern(ContextualPattern pattern); +/// Attempt to simplify an ExprPattern into a BoolPattern or +/// OptionalSomePattern. Returns \c nullptr if the pattern could not be +/// simplified. +NullablePtr trySimplifyExprPattern(ExprPattern *EP, Type patternTy); + /// Coerce a pattern to the given type. /// /// \param pattern The contextual pattern. /// \param type the type to coerce the pattern to. /// \param options Options that control the coercion. +/// \param tryRewritePattern A function that attempts to externally rewrite +/// the given pattern. This is used by the constraint system to take over +/// rewriting for ExprPatterns. /// /// \returns the coerced pattern, or nullptr if the coercion failed. -Pattern *coercePatternToType(ContextualPattern pattern, Type type, - TypeResolutionOptions options); +Pattern *coercePatternToType( + ContextualPattern pattern, Type type, TypeResolutionOptions options, + llvm::function_ref(Pattern *, Type)> tryRewritePattern = + [](Pattern *, Type) { return None; }); + bool typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, Type type); /// Coerce the specified parameter list of a ClosureExpr to the specified diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index 0104bbe045c16..b5b881928764a 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -564,3 +564,84 @@ struct TestIUOMatchOp { if case self = self {} } } + +struct TestRecursiveVarRef { + lazy var e: () -> Int = {e}() +} + +func testMultiStmtClosureExprPattern(_ x: Int) { + if case { (); return x }() = x {} +} + +func testExprPatternIsolation() { + // We type-check ExprPatterns separately, so these are illegal. + if case 0 = nil {} // expected-error {{'nil' requires a contextual type}} + let _ = { + if case 0 = nil {} // expected-error {{'nil' requires a contextual type}} + } + for case 0 in nil {} // expected-error {{'nil' requires a contextual type}} + for case 0 in [nil] {} + // expected-error@-1 {{type 'Any' cannot conform to 'Equatable'}} + // expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}} + // expected-note@-3 {{requirement from conditional conformance of 'Any?' to 'Equatable'}} + + // Though we will try Double for an integer literal... + let d: Double = 0 + if case d = 0 {} + let _ = { + if case d = 0 {} + } + for case d in [0] {} + + // But not Float + let f: Float = 0 + if case f = 0 {} // expected-error {{expression pattern of type 'Float' cannot match values of type 'Int'}} + let _ = { + if case f = 0 {} // expected-error {{expression pattern of type 'Float' cannot match values of type 'Int'}} + } + for case f in [0] {} // expected-error {{expression pattern of type 'Float' cannot match values of type 'Int'}} + + enum MultiPayload: Equatable { + case e(T, T) + static func f(_ x: T, _ y: T) -> Self { .e(x, y) } + } + enum E: Equatable { + case a, b + static var c: E { .a } + static var d: E { .b } + } + + func produceMultiPayload() -> MultiPayload { fatalError() } + + // We type-check ExprPatterns left to right, so only one of these works. + if case .e(0.0, 0) = produceMultiPayload() {} + if case .e(0, 0.0) = produceMultiPayload() {} // expected-error {{expression pattern of type 'Double' cannot match values of type 'Int'}} + + for case .e(0.0, 0) in [produceMultiPayload()] {} + for case .e(0, 0.0) in [produceMultiPayload()] {} // expected-error {{expression pattern of type 'Double' cannot match values of type 'Int'}} + + // Same, because although it's a top-level ExprPattern, we don't resolve + // that until during solving. + if case .f(0.0, 0) = produceMultiPayload() {} + if case .f(0, 0.0) = produceMultiPayload() {} // expected-error {{expression pattern of type 'Double' cannot match values of type 'Int'}} + + if case .e(5, nil) = produceMultiPayload() {} // expected-warning {{type 'Int' is not optional, value can never be nil; this is an error in Swift 6}} + + // FIXME: Bad error + if case .e(nil, 0) = produceMultiPayload() {} + // expected-error@-1 {{expression pattern of type 'String' cannot match values of type 'Substring'}} + // expected-note@-2 {{overloads for '~=' exist with these partially matching parameter lists}} + + if case .e(5, nil) = produceMultiPayload() as MultiPayload {} + if case .e(nil, 0) = produceMultiPayload() as MultiPayload {} + + // Enum patterns are solved together. + if case .e(E.a, .b) = produceMultiPayload() {} + if case .e(.a, E.b) = produceMultiPayload() {} + + // These also work because they start life as EnumPatterns. + if case .e(E.c, .d) = produceMultiPayload() {} + if case .e(.c, E.d) = produceMultiPayload() {} + for case .e(E.c, .d) in [produceMultiPayload()] {} + for case .e(.c, E.d) in [produceMultiPayload()] {} +} diff --git a/test/Constraints/rdar105781521.swift b/test/Constraints/rdar105781521.swift index ec76bf061d662..6e5f6eebe3092 100644 --- a/test/Constraints/rdar105781521.swift +++ b/test/Constraints/rdar105781521.swift @@ -12,7 +12,6 @@ func test(value: MyEnum) { switch value { case .first(true): // expected-error@-1 {{expression pattern of type 'Bool' cannot match values of type 'String'}} - // expected-note@-2 {{overloads for '~=' exist with these partially matching parameter lists: (Substring, String)}} break default: break diff --git a/test/Constraints/rdar105782480.swift b/test/Constraints/rdar105782480.swift new file mode 100644 index 0000000000000..581f7b3d0db3b --- /dev/null +++ b/test/Constraints/rdar105782480.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://105782480 +enum MyEnum { + case second(Int?) +} + +func takeClosure(_ x: () -> Void) {} + +func foo(value: MyEnum) { + takeClosure { + switch value { + case .second(let drag).invalid: + // expected-error@-1 {{value of type 'MyEnum' has no member 'invalid'}} + break + } + } +} diff --git a/test/Constraints/result_builder_diags.swift b/test/Constraints/result_builder_diags.swift index 77db71ac80812..5f3486b28be62 100644 --- a/test/Constraints/result_builder_diags.swift +++ b/test/Constraints/result_builder_diags.swift @@ -658,8 +658,6 @@ struct MyView { } @TupleBuilder var invalidCaseWithoutDot: some P { - // expected-error@-1 {{return type of property 'invalidCaseWithoutDot' requires that 'Either' conform to 'P'}} - // expected-note@-2 {{opaque return type declared here}} switch Optional.some(1) { case none: 42 // expected-error {{cannot find 'none' in scope}} case .some(let x): diff --git a/test/Constraints/result_builder_invalid_stmts.swift b/test/Constraints/result_builder_invalid_stmts.swift index 5de3ccc668b1f..5f8dc47268ee4 100644 --- a/test/Constraints/result_builder_invalid_stmts.swift +++ b/test/Constraints/result_builder_invalid_stmts.swift @@ -15,7 +15,6 @@ func foo(_ x: String) -> Int { if .random() { switch x { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} 0 default: 1 @@ -29,7 +28,6 @@ func bar(_ x: String) -> Int { case 0: switch x { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} 0 default: 1 @@ -44,7 +42,6 @@ func baz(_ x: String) -> Int { do { switch x { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} 0 default: 1 @@ -57,7 +54,6 @@ func qux(_ x: String) -> Int { for _ in 0 ... 0 { switch x { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists}} 0 default: 1 diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 10468d04b5dd0..0f6f36d5706b9 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -743,3 +743,42 @@ let _: DispatchTime = .#^UNRESOLVED_FUNCTION_CALL^#now() + 0.2 // UNRESOLVED_FUNCTION_CALL-DAG: Decl[StaticMethod]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: now()[#DispatchTime#]; // UNRESOLVED_FUNCTION_CALL-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init()[#DispatchTime#]; // UNRESOLVED_FUNCTION_CALL: End completions + +func id(_ x: T) -> T { x } + +func testNestedExprPatternCompletion(_ x: SomeEnum1) { + // Multi-statement closures have different type-checking code paths, + // so we need to test both. + let fn = { + switch x { + case id(.#^UNRESOLVED_NESTED1^#): + // UNRESOLVED_NESTED1: Begin completions, 3 items + // UNRESOLVED_NESTED1: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; name=South + // UNRESOLVED_NESTED1: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; name=North + // UNRESOLVED_NESTED1: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // UNRESOLVED_NESTED1: End completions + break + } + if case id(.#^UNRESOLVED_NESTED2^#) = x {} + // UNRESOLVED_NESTED2: Begin completions, 3 items + // UNRESOLVED_NESTED2: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; name=South + // UNRESOLVED_NESTED2: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; name=North + // UNRESOLVED_NESTED2: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // UNRESOLVED_NESTED2: End completions + } + switch x { + case id(.#^UNRESOLVED_NESTED3^#): + // UNRESOLVED_NESTED3: Begin completions, 3 items + // UNRESOLVED_NESTED3: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; name=South + // UNRESOLVED_NESTED3: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; name=North + // UNRESOLVED_NESTED3: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // UNRESOLVED_NESTED3: End completions + break + } + if case id(.#^UNRESOLVED_NESTED4^#) = x {} + // UNRESOLVED_NESTED4: Begin completions, 3 items + // UNRESOLVED_NESTED4: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: South[#SomeEnum1#]; name=South + // UNRESOLVED_NESTED4: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: North[#SomeEnum1#]; name=North + // UNRESOLVED_NESTED4: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // UNRESOLVED_NESTED4: End completions +} diff --git a/test/Parse/matching_patterns.swift b/test/Parse/matching_patterns.swift index 27970e69f67b5..1fbec74a906e8 100644 --- a/test/Parse/matching_patterns.swift +++ b/test/Parse/matching_patterns.swift @@ -307,8 +307,7 @@ do { while case let _ as [Derived] = arr {} // expected-warning@-1 {{'let' pattern has no effect; sub-pattern didn't bind any variables}} - // FIXME: https://github.com/apple/swift/issues/61850 - // expected-warning@+1 {{heterogeneous collection literal could only be inferred to '[[Base]]'; add explicit type annotation if this is intentional}} + // https://github.com/apple/swift/issues/61850 for case _ as [Derived] in [arr] {} if case is [Derived] = arr {} diff --git a/test/Parse/matching_patterns_reference_bindings.swift b/test/Parse/matching_patterns_reference_bindings.swift index 4c61ae1b243cf..fadfd53ac9821 100644 --- a/test/Parse/matching_patterns_reference_bindings.swift +++ b/test/Parse/matching_patterns_reference_bindings.swift @@ -328,8 +328,7 @@ do { while case let _ as [Derived] = arr {} // expected-warning@-1 {{'let' pattern has no effect; sub-pattern didn't bind any variables}} - // FIXME: https://github.com/apple/swift/issues/61850 - // expected-warning@+1 {{heterogeneous collection literal could only be inferred to '[[Base]]'; add explicit type annotation if this is intentional}} + // https://github.com/apple/swift/issues/61850 for case _ as [Derived] in [arr] {} if case is [Derived] = arr {} diff --git a/test/SILGen/switch_expr.swift b/test/SILGen/switch_expr.swift index 162dbe886e00f..4de051faf9cfb 100644 --- a/test/SILGen/switch_expr.swift +++ b/test/SILGen/switch_expr.swift @@ -323,3 +323,14 @@ func nestedType() throws -> Int { 0 } } + +func exprPatternInClosure() { + let f: (Int) -> Void = { i in + switch i { + case i: + () + default: + () + } + } +} diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 2472e6a2eaea6..2927e2e67cf83 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -1323,7 +1323,7 @@ struct MissingPropertyWrapperUnwrap { struct InvalidPropertyDelegateUse { // TODO(diagnostics): We need to a tailored diagnostic for extraneous arguments in property delegate initialization - @Foo var x: Int = 42 // expected-error@:21 {{argument passed to call that takes no arguments}} + @Foo var x: Int = 42 // expected-error@:21 {{extra argument 'wrappedValue' in call}} func test() { self.x.foo() // expected-error {{value of type 'Int' has no member 'foo'}} diff --git a/test/expr/closure/multi_statement.swift b/test/expr/closure/multi_statement.swift index 38c35ce3afdec..ed475d2b929fb 100644 --- a/test/expr/closure/multi_statement.swift +++ b/test/expr/closure/multi_statement.swift @@ -336,8 +336,8 @@ func test_unknown_refs_in_tilde_operator() { enum E { } - let _: (E) -> Void = { // expected-error {{unable to infer closure type in the current context}} - if case .test(unknown) = $0 { + let _: (E) -> Void = { + if case .test(unknown) = $0 { // expected-error {{type 'E' has no member 'test'}} // expected-error@-1 2 {{cannot find 'unknown' in scope}} } } diff --git a/test/stdlib/StringDiagnostics.swift b/test/stdlib/StringDiagnostics.swift index c3fde8ff884f5..3a53a26c69ee0 100644 --- a/test/stdlib/StringDiagnostics.swift +++ b/test/stdlib/StringDiagnostics.swift @@ -48,7 +48,6 @@ func testAmbiguousStringComparisons(s: String) { // Shouldn't suggest 'as' in a pattern-matching context, as opposed to all these other situations if case nsString = "" {} // expected-error{{expression pattern of type 'NSString' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists: (Substring, String)}} } func testStringDeprecation(hello: String) { diff --git a/test/stmt/errors.swift b/test/stmt/errors.swift index d71da201b4247..506b2230f775a 100644 --- a/test/stmt/errors.swift +++ b/test/stmt/errors.swift @@ -169,7 +169,8 @@ func thirteen() { thirteen_helper { (a) in // expected-error {{invalid conversion from throwing function of type '(Thirteen) throws -> ()' to non-throwing function type '(Thirteen) -> ()'}} do { try thrower() - } catch a { + // FIXME: Bad diagnostic (https://github.com/apple/swift/issues/63459) + } catch a { // expected-error {{binary operator '~=' cannot be applied to two 'any Error' operands}} } } } diff --git a/validation-test/Sema/SwiftUI/rdar70256351.swift b/validation-test/Sema/SwiftUI/rdar70256351.swift index 0881009bd7457..1aa7ad723e977 100644 --- a/validation-test/Sema/SwiftUI/rdar70256351.swift +++ b/validation-test/Sema/SwiftUI/rdar70256351.swift @@ -10,7 +10,6 @@ struct ContentView: View { var body: some View { switch currentPage { case 1: // expected-error {{expression pattern of type 'Int' cannot match values of type 'String'}} - // expected-note@-1 {{overloads for '~=' exist with these partially matching parameter lists: (Substring, String)}} Text("1") default: Text("default") diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar92327807.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar92327807.swift index 0b367a644103b..26c5bd4fcf867 100644 --- a/validation-test/Sema/type_checker_crashers_fixed/rdar92327807.swift +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar92327807.swift @@ -10,7 +10,7 @@ func test(result: MyEnum, optResult: MyEnum?) { } if let .co(42) = result { // expected-error {{pattern matching in a condition requires the 'case' keyword}} - // expected-error@-1 {{type of expression is ambiguous without more context}} + // expected-error@-1 {{type 'MyEnum' has no member 'co'}} } if let .co = optResult { // expected-error {{pattern matching in a condition requires the 'case' keyword}}