diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d0e7a3fa5105e..3716b949a6ff4 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -197,6 +197,9 @@ ERROR(cannot_match_expr_tuple_pattern_with_nontuple_value,none, ERROR(cannot_match_unresolved_expr_pattern_with_value,none, "pattern cannot match values of type %0", (Type)) +ERROR(cannot_match_value_with_pattern,none, + "pattern of type %1 cannot match %0", + (Type, Type)) ERROR(cannot_reference_compare_types,none, "cannot check reference equality of functions; operands here have types " 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/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h index 71fd781385d97..981b70188ebfc 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -318,6 +318,9 @@ class ConstraintLocator : public llvm::FoldingSetNode { /// otherwise \c nullptr. NullablePtr getPatternMatch() const; + /// Whether the locator in question is for a pattern match. + bool isForPatternMatch() const; + /// Returns true if \p locator is ending with either of the following /// - Member /// - Member -> KeyPathDynamicMember diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 62649588386f0..c60640e114cd9 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -225,6 +225,10 @@ CUSTOM_LOCATOR_PATH_ELT(TernaryBranch) /// Performing a pattern patch. CUSTOM_LOCATOR_PATH_ELT(PatternMatch) +/// The constraint that models the allowed implicit casts for +/// an EnumElementPattern. +SIMPLE_LOCATOR_PATH_ELT(EnumPatternImplicitCastMatch) + /// 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 1951e3ad1c05d..6d6bb23a2712c 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -1500,6 +1500,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; @@ -1685,6 +1689,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; @@ -2148,6 +2162,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; @@ -2745,6 +2763,9 @@ class ConstraintSystem { /// The length of \c caseLabelItems. unsigned numCaseLabelItems; + /// The length of \c exprPatterns. + unsigned numExprPatterns; + /// The length of \c isolatedParams. unsigned numIsolatedParams; @@ -3166,6 +3187,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); @@ -4315,6 +4345,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/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 43fa783a0d786..b6ae98679227c 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -984,7 +984,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 f6782775c5f23..f64ee2a0a82cb 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8705,6 +8705,9 @@ namespace { return Action::SkipChildren(); } + NullablePtr + rewritePattern(Pattern *pattern, DeclContext *DC); + /// Rewrite the target, producing a new target. Optional rewriteTarget(SyntacticElementTarget target); @@ -8951,12 +8954,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) { @@ -9021,10 +9080,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; @@ -9171,10 +9234,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; @@ -9262,7 +9330,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; @@ -9353,47 +9422,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) { @@ -9461,8 +9494,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 fc9b3cc28d9e3..241c849d5a80a 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2715,6 +2715,19 @@ bool ContextualFailure::diagnoseAsError() { return false; } + case ConstraintLocator::EnumPatternImplicitCastMatch: { + // In this case, the types are reversed, as we are checking whether we + // can convert the pattern type to the context type. + std::swap(fromType, toType); + diagnostic = diag::cannot_match_value_with_pattern; + break; + } + + case ConstraintLocator::PatternMatch: { + diagnostic = diag::cannot_match_value_with_pattern; + break; + } + default: return false; } @@ -2927,9 +2940,11 @@ bool ContextualFailure::diagnoseConversionToNil() const { void ContextualFailure::tryFixIts(InFlightDiagnostic &diagnostic) const { auto *locator = getLocator(); // Can't apply any of the fix-its below if this failure - // is related to `inout` argument. - if (locator->isLastElement()) + // is related to `inout` argument, or a pattern mismatch. + if (locator->isLastElement() || + locator->isForPatternMatch()) { return; + } if (trySequenceSubsequenceFixIts(diagnostic)) return; @@ -3549,6 +3564,8 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, return diag::cannot_convert_discard_value; case CTP_CaseStmt: + return diag::cannot_match_value_with_pattern; + case CTP_ThrowStmt: case CTP_ForEachStmt: case CTP_ForEachSequence: @@ -8756,7 +8773,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 efcfbc8123cd8..c30c94dbc2792 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2438,37 +2438,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; }; @@ -2476,33 +2460,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); } @@ -2526,9 +2495,7 @@ namespace { locator.withPathElement(LocatorPathElt::PatternMatch(pattern)); // 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 { @@ -2608,18 +2575,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 @@ -2691,9 +2653,6 @@ namespace { Type type = TypeChecker::typeCheckPattern(contextualPattern); - if (!type) - return Type(); - // Look through reference storage types. type = type->getReferenceStorageReferent(); @@ -2708,13 +2667,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 @@ -2728,41 +2687,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())); } @@ -2771,28 +2705,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)); } @@ -2803,22 +2721,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 @@ -2828,9 +2730,26 @@ namespace { auto isType = CS.createTypeVariable(CS.getConstraintLocator(pattern), TVO_CanBindToNoEscape | TVO_CanBindToHole); + + // 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); } @@ -2864,6 +2783,9 @@ namespace { if (dyn_cast_or_null(enumPattern->getSubPattern())) functionRefKind = FunctionRefKind::Compound; + auto patternLocator = + locator.withPathElement(LocatorPathElt::PatternMatch(pattern)); + if (enumPattern->getParentType() || enumPattern->getParentTypeRepr()) { // Resolve the parent type. const auto parentType = [&] { @@ -2889,42 +2811,25 @@ namespace { TypeResolverContext::InExpression, patternMatchLoc); }(); - if (!parentType) - return Type(); - // Perform member lookup into the parent's metatype. Type parentMetaType = MetatypeType::get(parentType); - CS.addValueMemberConstraint( - parentMetaType, enumPattern->getName(), memberType, CurDC, - functionRefKind, {}, - CS.getConstraintLocator(locator, - LocatorPathElt::PatternMatch(pattern))); + CS.addValueMemberConstraint(parentMetaType, enumPattern->getName(), + memberType, CurDC, functionRefKind, {}, + patternLocator); // Parent type needs to be convertible to the pattern type; this // accounts for cases where the pattern type is existential. CS.addConstraint( ConstraintKind::Conversion, parentType, patternType, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + patternLocator.withPathElement( + ConstraintLocator::EnumPatternImplicitCastMatch)); 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( MetatypeType::get(patternType), enumPattern->getName(), - memberType, CurDC, functionRefKind, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + memberType, CurDC, functionRefKind, patternLocator); baseType = patternType; } @@ -2936,10 +2841,7 @@ namespace { Type subPatternType = getTypeForPattern( subPattern, locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)), - Type(), bindPatternVarsOneWay); - - if (!subPatternType) - return Type(); + bindPatternVarsOneWay); SmallVector params; decomposeTuple(subPatternType, params); @@ -2957,28 +2859,29 @@ 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. - CS.addConstraint( - ConstraintKind::Equal, functionType, memberType, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); - CS.addConstraint( - ConstraintKind::Conversion, outputType, baseType, - locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); + // 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, not 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, + patternLocator); + + CS.addConstraint(ConstraintKind::Conversion, outputType, baseType, + patternLocator); } 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 | TVO_CanBindToHole)); + 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"); @@ -4421,16 +4324,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( @@ -4845,8 +4750,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 2a638c3a8c06f..b2a7e3b74ec95 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2165,7 +2165,9 @@ class TupleMatcher { const auto &elt2 = tuple2->getElement(i); if (inPatternMatchingContext) { - 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; } else { // If the names don't match, we have a conflict. @@ -3523,21 +3525,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; } @@ -6367,8 +6360,20 @@ bool ConstraintSystem::repairFailures( break; } + case ConstraintLocator::EnumPatternImplicitCastMatch: { + // If either type is a placeholder, consider this fixed. + if (lhs->isPlaceholder() || rhs->isPlaceholder()) + return true; + + conversionsOrFixes.push_back(ContextualMismatch::create( + *this, lhs, rhs, getConstraintLocator(locator))); + break; + } + case ConstraintLocator::PatternMatch: { auto *pattern = elt.castTo().getPattern(); + + // TODO: We ought to introduce a new locator element for this. bool isMemberMatch = lhs->is() && isa(pattern); @@ -6383,13 +6388,12 @@ bool ConstraintSystem::repairFailures( if (lhs->isPlaceholder() || rhs->isPlaceholder()) return true; - // If member reference didn't match expected pattern, - // let's consider that a contextual mismatch. if (isMemberMatch) { recordAnyTypeVarAsPotentialHole(lhs); recordAnyTypeVarAsPotentialHole(rhs); conversionsOrFixes.push_back(AllowAssociatedValueMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); + break; } // `weak` declaration with an explicit non-optional type e.g. @@ -6402,7 +6406,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))); @@ -6411,6 +6415,9 @@ bool ConstraintSystem::repairFailures( } } + conversionsOrFixes.push_back(ContextualMismatch::create( + *this, lhs, rhs, getConstraintLocator(locator))); + break; } @@ -14833,8 +14840,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 e80def2f385f1..97dcdef70744b 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -195,6 +195,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()); @@ -327,6 +328,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); } @@ -621,6 +625,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(); @@ -737,6 +742,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 43e0ac33a50c9..8fdec6b319a5d 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -410,7 +410,7 @@ ElementInfo makeJoinElement(ConstraintSystem &cs, TypeJoinExpr *join, struct SyntacticElementContext : public llvm::PointerUnion { + SingleValueStmtExpr *, ExprPattern *> { // Inherit the constructors from PointerUnion. using PointerUnion::PointerUnion; @@ -441,6 +441,10 @@ struct SyntacticElementContext return context; } + static SyntacticElementContext forExprPattern(ExprPattern *EP) { + return SyntacticElementContext{EP}; + } + DeclContext *getAsDeclContext() const { if (auto *fn = this->dyn_cast()) { return fn; @@ -448,6 +452,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"); } @@ -519,7 +525,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)) { + 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(); @@ -535,7 +566,7 @@ class SyntacticElementConstraintGenerator } if (isa(stmt)) { - visitCaseItemPattern(pattern, context); + visitCaseItemPattern(pattern, contextInfo); return; } } @@ -626,8 +657,11 @@ class SyntacticElementConstraintGenerator // Convert the contextual type to the pattern, which establishes the // bindings. - cs.addConstraint(ConstraintKind::Conversion, context.getType(), patternType, - locator); + auto *loc = cs.getConstraintLocator( + locator, {LocatorPathElt::PatternMatch(pattern), + LocatorPathElt::ContextualType(context.purpose)}); + cs.addConstraint(ConstraintKind::Equal, context.getType(), patternType, + loc); // For any pattern variable that has a parent variable (i.e., another // pattern variable with the same name in the same case), require that @@ -1438,6 +1472,24 @@ bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) { return generator.hadError; } +void ConstraintSystem::generateConstraints(ArrayRef exprPatterns, + ConstraintLocatorBuilder locator) { + // Form a conjunction of ExprPattern elements, isolated from the rest of the + // pattern. + SmallVector elements; + SmallVector referencedTypeVars; + for (auto *EP : exprPatterns) { + auto ty = getType(EP)->castTo(); + referencedTypeVars.push_back(ty); + + ContextualTypeInfo context(ty, CTP_ExprPattern); + elements.push_back(makeElement(EP, getConstraintLocator(EP), context)); + } + auto *loc = getConstraintLocator(locator); + createConjunction(*this, elements, loc, /*isIsolated*/ true, + referencedTypeVars); +} + bool ConstraintSystem::isInResultBuilderContext(ClosureExpr *closure) const { if (!closure->hasSingleExpressionBody()) { auto *DC = closure->getParent(); @@ -1488,6 +1540,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 e3fe38458601b..4987c3a47cb21 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -90,6 +90,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ImplicitCallAsFunction: case ConstraintLocator::TernaryBranch: case ConstraintLocator::PatternMatch: + case ConstraintLocator::EnumPatternImplicitCastMatch: case ConstraintLocator::ArgumentAttribute: case ConstraintLocator::UnresolvedMemberChainResult: case ConstraintLocator::PlaceholderType: @@ -403,6 +404,10 @@ void LocatorPathElt::dump(raw_ostream &out) const { out << "pattern match"; break; + case ConstraintLocator::EnumPatternImplicitCastMatch: + out << "enum pattern implicit cast match"; + break; + case ConstraintLocator::ArgumentAttribute: { using AttrLoc = LocatorPathElt::ArgumentAttribute; @@ -677,6 +682,10 @@ NullablePtr ConstraintLocator::getPatternMatch() const { return matchElt->getPattern(); } +bool ConstraintLocator::isForPatternMatch() const { + return getPatternMatch() != nullptr; +} + bool ConstraintLocator::isMemberRef() const { if (isLastElement()) { return true; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 36b34ccbd4c84..10563c1fe10f8 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -5824,6 +5824,11 @@ void constraints::simplifyLocator(ASTNode &anchor, continue; } + case ConstraintLocator::EnumPatternImplicitCastMatch: { + path = path.slice(1); + continue; + } + case ConstraintLocator::PackType: case ConstraintLocator::ParentType: case ConstraintLocator::KeyPathType: 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/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 946e86da0d858..69260e9a0975e 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); } } } @@ -1547,9 +1570,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, type, parentTy, CheckedCastContextKind::EnumElementPattern, dc); // If the cast failed, we can't resolve the pattern. if (foundCastKind < CheckedCastKind::First_Resolved) { - diags - .diagnose(EEP->getLoc(), diag::downcast_to_unrelated, type, - parentTy) + diags.diagnose(EEP->getLoc(), diag::cannot_match_value_with_pattern, + type, parentTy) .highlight(EEP->getSourceRange()); return nullptr; } @@ -1559,9 +1581,9 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, castKind = foundCastKind; enumTy = parentTy; } else { - diags.diagnose(EEP->getLoc(), - diag::enum_element_pattern_not_member_of_enum, - EEP->getName(), type); + diags.diagnose(EEP->getLoc(), diag::cannot_match_value_with_pattern, + type, parentTy) + .highlight(EEP->getSourceRange()); return nullptr; } } @@ -1595,7 +1617,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, sub = coercePatternToType( pattern.forSubPattern(sub, /*retainTopLevel=*/false), elementType, - newSubOptions); + newSubOptions, tryRewritePattern); if (!sub) return nullptr; @@ -1630,7 +1652,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 +1696,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 6538ae8cd4f87..4cba8b17db863 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -733,15 +733,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..7305d549e6fdf 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -120,7 +120,7 @@ case iPadHair.HairForceOne: () case iPadHair.HairForceOne: // expected-error{{generic enum type 'iPadHair' is ambiguous without explicit generic parameters when matching value of type 'any HairType'}} () -case Watch.Edition: // expected-warning {{cast from 'any HairType' to unrelated type 'Watch' always fails}} +case Watch.Edition: // expected-error {{pattern of type 'Watch' cannot match 'any HairType'}} () case .HairForceOne: // expected-error{{type 'any HairType' has no member 'HairForceOne'}} () @@ -564,3 +564,97 @@ 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 (https://github.com/apple/swift/issues/64279) + 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()] {} + + // Silly, but allowed. + if case 0: Int? = 0 {} // expected-warning {{non-optional expression of type 'Int' used in a check for optionals}} + + var opt: Int? + if case opt = 0 {} +} + +enum LotsOfOptional { + case yup(Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?, Int?) +} +func testLotsOfNil(_ x: LotsOfOptional) { + if case .yup(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) = x {} +} 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/rdar106598067.swift b/test/Constraints/rdar106598067.swift new file mode 100644 index 0000000000000..941bbae2f2a2e --- /dev/null +++ b/test/Constraints/rdar106598067.swift @@ -0,0 +1,11 @@ +// RUN: %target-typecheck-verify-swift + +enum E: Error { case e } + +// rdar://106598067 – Make sure we don't crash. +// FIXME: Bad diagnostic (the issue is that it should be written 'as', not 'as?') +let fn = { + // expected-error@-1 {{unable to infer closure type in the current context}} + do {} catch let x as? E {} + // expected-warning@-1 {{'catch' block is unreachable because no errors are thrown in 'do' block}} +} diff --git a/test/Constraints/rdar107420031.swift b/test/Constraints/rdar107420031.swift new file mode 100644 index 0000000000000..2f5687eb6fe31 --- /dev/null +++ b/test/Constraints/rdar107420031.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift + +enum E { + case e +} + +func ~= (lhs: any Error, rhs: E) -> Bool { true } + +// rdar://107420031 – Make sure we don't crash. +// TODO: This ought to compile. +func foo(_ error: any Error) { + switch error { + case E.e: // expected-error {{pattern of type 'E' cannot match 'any Error'}} + break + default: + break + } +} diff --git a/test/Constraints/rdar107709341.swift b/test/Constraints/rdar107709341.swift new file mode 100644 index 0000000000000..cc38962ec4f12 --- /dev/null +++ b/test/Constraints/rdar107709341.swift @@ -0,0 +1,13 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://107709341 – Make sure we don't crash. +func foo(_ x: Int) { + let _ = { + switch x { + case Optional.some(x): // expected-error {{pattern of type 'Optional' cannot match 'Int'}} {{none}} + break + default: + break + } + } +} diff --git a/test/Constraints/rdar109419240.swift b/test/Constraints/rdar109419240.swift new file mode 100644 index 0000000000000..9d78a4c6cf831 --- /dev/null +++ b/test/Constraints/rdar109419240.swift @@ -0,0 +1,15 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://109419240 – Make sure we don't crash +enum E { // expected-note {{'E' declared here}} + case e(Int) +} + +func foo(_ arr: [E]) -> Int { + return arr.reduce(0) { (total, elem) -> Int in + switch elem { + case let e(x): // expected-error {{cannot find 'e' in scope; did you mean 'E'?}} + return total + x + } + } +} diff --git a/test/Constraints/result_builder_diags.swift b/test/Constraints/result_builder_diags.swift index 660e719a0e569..5cd49c0cc771a 100644 --- a/test/Constraints/result_builder_diags.swift +++ b/test/Constraints/result_builder_diags.swift @@ -659,8 +659,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 0e29f7935297e..f81f9a6c31ade 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -710,3 +710,38 @@ let _: DispatchTime = .#^UNRESOLVED_FUNCTION_CALL^#now() + 0.2 // UNRESOLVED_FUNCTION_CALL: Begin completions, 2 items // UNRESOLVED_FUNCTION_CALL-DAG: Decl[StaticMethod]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: now()[#DispatchTime#]; // UNRESOLVED_FUNCTION_CALL-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init()[#DispatchTime#]; + +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(:) + 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(:) + } + 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(:) + 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(:) +} diff --git a/test/Parse/matching_patterns.swift b/test/Parse/matching_patterns.swift index 932fb84e550c8..050916fac0574 100644 --- a/test/Parse/matching_patterns.swift +++ b/test/Parse/matching_patterns.swift @@ -122,7 +122,7 @@ if case let .Naught(value1, value2, value3) = n {} // expected-error{{pattern wi switch n { -case Foo.A: // expected-error{{enum case 'A' is not a member of type 'Voluntary'}} +case Foo.A: // expected-error{{pattern of type 'Foo' cannot match 'Voluntary'}} () case Voluntary.Naught, Voluntary.Naught(), // expected-error {{pattern with associated values does not match enum case 'Naught'}} @@ -307,6 +307,7 @@ do { while case let _ as [Derived] = arr {} // expected-warning@-1 {{'let' pattern has no effect; sub-pattern didn't bind any variables}} + // 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 906bea74dfae0..fbf6b9840b1b2 100644 --- a/test/Parse/matching_patterns_reference_bindings.swift +++ b/test/Parse/matching_patterns_reference_bindings.swift @@ -140,7 +140,7 @@ if case inout .Naught(value1, value2, value3) = n {} // expected-error{{pattern switch n { -case Foo.A: // expected-error{{enum case 'A' is not a member of type 'Voluntary'}} +case Foo.A: // expected-error{{pattern of type 'Foo' cannot match 'Voluntary'}} () case Voluntary.Naught, Voluntary.Naught(), // expected-error {{pattern with associated values does not match enum case 'Naught'}} @@ -328,6 +328,7 @@ do { while case let _ as [Derived] = arr {} // expected-warning@-1 {{'let' pattern has no effect; sub-pattern didn't bind any variables}} + // 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 7275aa1bb534d..310dc90329ab3 100644 --- a/test/SILGen/switch_expr.swift +++ b/test/SILGen/switch_expr.swift @@ -605,3 +605,14 @@ struct TestLValues { opt![keyPath: kp] = switch Bool.random() { case true: 1 case false: throw Err() } } } + +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 fa33d07393543..33acdbf13df9a 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -1338,7 +1338,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/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/issue-65360.swift b/validation-test/Sema/type_checker_crashers_fixed/issue-65360.swift index 2e7e10322c932..fd6652a85f4e4 100644 --- a/validation-test/Sema/type_checker_crashers_fixed/issue-65360.swift +++ b/validation-test/Sema/type_checker_crashers_fixed/issue-65360.swift @@ -9,12 +9,24 @@ let _: () -> Void = { let _: () -> Void = { for case (0)? in [a] {} + // expected-error@-1 {{pattern cannot match values of type 'Any?'}} if case (0, 0) = a {} - // expected-error@-1 {{cannot convert value of type 'Any?' to specified type '(_, _)}} } let _: () -> Void = { for case (0)? in [a] {} + // expected-error@-1 {{pattern cannot match values of type 'Any?'}} for case (0, 0) in [a] {} - // expected-error@-1 {{cannot convert value of type 'Any?' to expected element type '(_, _)'}} +} + +let _: () -> Void = { + if case (0, 0) = a {} + // expected-error@-1 {{cannot convert value of type 'Any?' to specified type '(Int, Int)'}} + for case (0)? in [a] {} +} + +let _: () -> Void = { + for case (0, 0) in [a] {} + // expected-error@-1 {{cannot convert value of type 'Any?' to expected element type '(Int, Int)'}} + for case (0)? in [a] {} }