diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8eefcc6ca7d62..97bf7063ca4a4 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/AST/Types.h b/include/swift/AST/Types.h index 07fd0a66dcbe2..11984c7076d75 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -173,8 +173,14 @@ class RecursiveTypeProperties { /// This type contains an ElementArchetype. HasElementArchetype = 0x4000, + /// Whether the type is allocated in the constraint solver arena. This can + /// differ from \c HasTypeVariable for types such as placeholders, which do + /// not have type variables, but we still want to allocate in the solver if + /// they have a type variable originator. + SolverAllocated = 0x8000, + /// This type contains a concrete pack. - HasConcretePack = 0x8000, + HasConcretePack = 0x10000, Last_Property = HasConcretePack }; @@ -227,6 +233,12 @@ class RecursiveTypeProperties { /// opened element archetype? bool hasElementArchetype() const { return Bits & HasElementArchetype; } + /// Whether the type is allocated in the constraint solver arena. This can + /// differ from \c hasTypeVariable() for types such as placeholders, which + /// do not have type variables, but we still want to allocate in the solver if + /// they have a type variable originator. + bool isSolverAllocated() const { return Bits & SolverAllocated; } + /// Does a type with these properties structurally contain a local /// archetype? bool hasLocalArchetype() const { @@ -405,12 +417,12 @@ class alignas(1 << TypeAlignInBits) TypeBase NumProtocols : 16 ); - SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 7+31, + SWIFT_INLINE_BITFIELD_FULL(TypeVariableType, TypeBase, 7+30, /// Type variable options. Options : 7, : NumPadBits, /// The unique number assigned to this type variable. - ID : 31 + ID : 30 ); SWIFT_INLINE_BITFIELD(SILFunctionType, TypeBase, NumSILExtInfoBits+1+4+1+2+1+1, @@ -506,6 +518,8 @@ class alignas(1 << TypeAlignInBits) TypeBase } void setRecursiveProperties(RecursiveTypeProperties properties) { + assert(!(properties.hasTypeVariable() && !properties.isSolverAllocated()) && + "type variables must be solver allocated!"); Bits.TypeBase.Properties = properties.getBits(); assert(Bits.TypeBase.Properties == properties.getBits() && "Bits dropped!"); } @@ -6726,8 +6740,9 @@ TypeVariableType : public TypeBase { // type is opaque. TypeVariableType(const ASTContext &C, unsigned ID) - : TypeBase(TypeKind::TypeVariable, &C, - RecursiveTypeProperties::HasTypeVariable) { + : TypeBase(TypeKind::TypeVariable, &C, + RecursiveTypeProperties::HasTypeVariable | + RecursiveTypeProperties::SolverAllocated) { // Note: the ID may overflow (current limit is 2^20 - 1). Bits.TypeVariableType.ID = ID; if (Bits.TypeVariableType.ID != ID) { @@ -6786,6 +6801,8 @@ DEFINE_EMPTY_CAN_TYPE_WRAPPER(TypeVariableType, Type) /// because the expression is ambiguous. This type is only used by the /// constraint solver and transformed into UnresolvedType to be used in AST. class PlaceholderType : public TypeBase { + // NOTE: If you add a new Type-based originator, you'll need to update the + // recursive property logic in PlaceholderType::get. using Originator = llvm::PointerUnion; diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index 32bca5b7fad71..55d9349726ec3 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -401,6 +401,9 @@ enum class FixKind : uint8_t { /// Produce a warning for a tuple label mismatch. AllowTupleLabelMismatch, + /// Allow an associated value mismatch for an enum element pattern. + AllowAssociatedValueMismatch, + /// Produce an error for not getting a compile-time constant NotCompileTimeConst, @@ -3241,6 +3244,28 @@ class AllowTupleLabelMismatch final : public ContextualMismatch { } }; +class AllowAssociatedValueMismatch final : public ContextualMismatch { + AllowAssociatedValueMismatch(ConstraintSystem &cs, Type fromType, Type toType, + ConstraintLocator *locator) + : ContextualMismatch(cs, FixKind::AllowAssociatedValueMismatch, fromType, + toType, locator) {} + +public: + std::string getName() const override { + return "allow associated value mismatch"; + } + + bool diagnose(const Solution &solution, bool asNote = false) const override; + + static AllowAssociatedValueMismatch *create(ConstraintSystem &cs, + Type fromType, Type toType, + ConstraintLocator *locator); + + static bool classof(const ConstraintFix *fix) { + return fix->getKind() == FixKind::AllowAssociatedValueMismatch; + } +}; + class AllowNonOptionalWeak final : public ConstraintFix { AllowNonOptionalWeak(ConstraintSystem &cs, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::AllowNonOptionalWeak, locator) {} 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 98934377ba3ed..981b70188ebfc 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -18,11 +18,12 @@ #ifndef SWIFT_SEMA_CONSTRAINTLOCATOR_H #define SWIFT_SEMA_CONSTRAINTLOCATOR_H -#include "swift/Basic/Debug.h" -#include "swift/Basic/LLVM.h" #include "swift/AST/ASTNode.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" +#include "swift/Basic/Debug.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/NullablePtr.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerIntPair.h" @@ -313,6 +314,13 @@ class ConstraintLocator : public llvm::FoldingSetNode { /// branch, and if so, the kind of branch. Optional isForSingleValueStmtBranch() const; + /// If the locator in question is for a pattern match, returns the pattern, + /// 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 99137dad99bcd..1034c6b8848b5 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/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index a11f533378298..77dfb2283302d 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -431,6 +431,7 @@ struct ASTContext::Implementation { llvm::DenseMap InOutTypes; llvm::DenseMap, DependentMemberType *> DependentMemberTypes; + llvm::DenseMap PlaceholderTypes; llvm::DenseMap DynamicSelfTypes; llvm::DenseMap, EnumType*> EnumTypes; llvm::DenseMap, StructType*> StructTypes; @@ -1800,9 +1801,8 @@ bool ASTContext::hadError() const { /// Retrieve the arena from which we should allocate storage for a type. static AllocationArena getArena(RecursiveTypeProperties properties) { - bool hasTypeVariable = properties.hasTypeVariable(); - return hasTypeVariable ? AllocationArena::ConstraintSolver - : AllocationArena::Permanent; + return properties.isSolverAllocated() ? AllocationArena::ConstraintSolver + : AllocationArena::Permanent; } void ASTContext::addSearchPath(StringRef searchPath, bool isFramework, @@ -3118,15 +3118,43 @@ Type ErrorType::get(Type originalType) { void *mem = ctx.Allocate(sizeof(ErrorType) + sizeof(Type), alignof(ErrorType), arena); RecursiveTypeProperties properties = RecursiveTypeProperties::HasError; - if (originalProperties.hasTypeVariable()) - properties |= RecursiveTypeProperties::HasTypeVariable; + + // We need to preserve the solver allocated bit, to ensure any wrapping + // types are solver allocated too. + if (originalProperties.isSolverAllocated()) + properties |= RecursiveTypeProperties::SolverAllocated; + return entry = new (mem) ErrorType(ctx, originalType, properties); } Type PlaceholderType::get(ASTContext &ctx, Originator originator) { assert(originator); - return new (ctx, AllocationArena::Permanent) - PlaceholderType(ctx, originator, RecursiveTypeProperties::HasPlaceholder); + + auto originatorProps = [&]() -> RecursiveTypeProperties { + if (auto *tv = originator.dyn_cast()) + return tv->getRecursiveProperties(); + + if (auto *depTy = originator.dyn_cast()) + return depTy->getRecursiveProperties(); + + return RecursiveTypeProperties(); + }(); + auto arena = getArena(originatorProps); + + auto &cache = ctx.getImpl().getArena(arena).PlaceholderTypes; + auto &entry = cache[originator.getOpaqueValue()]; + if (entry) + return entry; + + RecursiveTypeProperties properties = RecursiveTypeProperties::HasPlaceholder; + + // We need to preserve the solver allocated bit, to ensure any wrapping + // types are solver allocated too. + if (originatorProps.isSolverAllocated()) + properties |= RecursiveTypeProperties::SolverAllocated; + + entry = new (ctx, arena) PlaceholderType(ctx, originator, properties); + return entry; } BuiltinIntegerType *BuiltinIntegerType::get(BuiltinIntegerWidth BitWidth, @@ -3944,7 +3972,7 @@ isAnyFunctionTypeCanonical(ArrayRef params, static RecursiveTypeProperties getGenericFunctionRecursiveProperties(ArrayRef params, Type result) { - static_assert(RecursiveTypeProperties::BitWidth == 16, + static_assert(RecursiveTypeProperties::BitWidth == 17, "revisit this if you add new recursive type properties"); RecursiveTypeProperties properties; @@ -4604,7 +4632,7 @@ CanSILFunctionType SILFunctionType::get( void *mem = ctx.Allocate(bytes, alignof(SILFunctionType)); RecursiveTypeProperties properties; - static_assert(RecursiveTypeProperties::BitWidth == 16, + static_assert(RecursiveTypeProperties::BitWidth == 17, "revisit this if you add new recursive type properties"); for (auto ¶m : params) properties |= param.getInterfaceType()->getRecursiveProperties(); 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/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 256af375bc9b9..7c4617f31708b 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1743,6 +1743,16 @@ Parser::parseStmtConditionElement(SmallVectorImpl &result, auto diagLoc = ThePattern.get()->getSemanticsProvidingPattern()->getStartLoc(); diagnose(diagLoc, diag::conditional_var_valid_identifiers_only) .fixItInsert(diagLoc, "<#identifier#> = "); + + // For better recovery, assume the expression pattern as the initializer, + // and synthesize an optional AnyPattern. + auto *semanticPattern = ThePattern.get()->getSemanticsProvidingPattern(); + if (auto *EP = dyn_cast(semanticPattern)) { + Init = makeParserResult(EP->getSubExpr()); + auto *AP = AnyPattern::createImplicit(Context); + ThePattern = + makeParserResult(OptionalSomePattern::createImplicit(Context, AP)); + } } else { diagnose(Tok, diag::conditional_var_initializer_required); } 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 df09c8f141f61..3737d91912fd2 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8532,8 +8532,8 @@ namespace { exprType = solution.simplifyType(exprType); // assert((!expr->getType() || expr->getType()->isEqual(exprType)) && // "Mismatched types!"); - assert(!exprType->hasTypeVariable() && - "Should not write type variable into expression!"); + assert(!exprType->getRecursiveProperties().isSolverAllocated() && + "Should not write solver allocated type into expression!"); assert(!exprType->hasPlaceholder() && "Should not write type placeholders into expression!"); expr->setType(exprType); @@ -8543,8 +8543,10 @@ namespace { Type componentType; if (cs.hasType(kp, i)) { componentType = solution.simplifyType(cs.getType(kp, i)); - assert(!componentType->hasTypeVariable() && - "Should not write type variable into key-path component"); + assert( + !componentType->getRecursiveProperties().isSolverAllocated() && + "Should not write solver allocated type into key-path " + "component!"); assert(!componentType->hasPlaceholder() && "Should not write type placeholder into key-path component"); kp->getMutableComponents()[i].setComponentType(componentType); @@ -8701,6 +8703,9 @@ namespace { return Action::SkipChildren(); } + NullablePtr + rewritePattern(Pattern *pattern, DeclContext *DC); + /// Rewrite the target, producing a new target. Optional rewriteTarget(SyntacticElementTarget target); @@ -8947,12 +8952,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) { @@ -9017,10 +9078,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; @@ -9167,10 +9232,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; @@ -9258,7 +9328,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; @@ -9349,47 +9420,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) { @@ -9457,8 +9492,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/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 2e1e81aa2b2cf..0c8c28b70c337 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -2204,16 +2204,25 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const { return std::make_pair(fix, /*impact=*/(unsigned)10); } - if (auto pattern = getAsPattern(dstLocator->getAnchor())) { - if (dstLocator->getPath().size() == 1 && - dstLocator->isLastElement()) { + if (auto pattern = dstLocator->getPatternMatch()) { + if (dstLocator->isLastElement()) { + // If this is the pattern in a for loop, and we have a mismatch of the + // element type, then we don't have any useful contextual information + // for the pattern, and can just bind to a hole without needing to penalize + // the solution further. + auto *seqLoc = cs.getConstraintLocator( + dstLocator->getAnchor(), ConstraintLocator::SequenceElementType); + if (cs.hasFixFor(seqLoc, + FixKind::IgnoreCollectionElementContextualMismatch)) { + return None; + } // Not being able to infer the type of a variable in a pattern binding // decl is more dramatic than anything that could happen inside the // expression because we want to preferrably point the diagnostic to a // part of the expression that caused us to be unable to infer the // variable's type. ConstraintFix *fix = - IgnoreUnresolvedPatternVar::create(cs, pattern, dstLocator); + IgnoreUnresolvedPatternVar::create(cs, pattern.get(), dstLocator); return std::make_pair(fix, /*impact=*/(unsigned)100); } } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 695b361a83655..dbdb24db2f6b9 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -562,7 +562,10 @@ bool MissingConformanceFailure::diagnoseAsError() { llvm::SmallPtrSet anchors; for (const auto *fix : getSolution().Fixes) { if (auto anchor = fix->getAnchor()) { - if (anchor.is()) + auto path = fix->getLocator()->getPath(); + SourceRange range; + simplifyLocator(anchor, path, range); + if (anchor && anchor.is()) anchors.insert(getAsExpr(anchor)); } } @@ -703,10 +706,15 @@ bool MissingConformanceFailure::diagnoseAsAmbiguousOperatorRef() { if (!ODRE) return false; + auto isStandardType = [](Type ty) { + return ty->isStdlibType() || ty->is(); + }; + auto name = ODRE->getDecls().front()->getBaseName(); - if (!(name.isOperator() && getLHS()->isStdlibType() && getRHS()->isStdlibType())) + if (!(name.isOperator() && isStandardType(getLHS()) && + isStandardType(getRHS()))) { return false; - + } // If this is an operator reference and both types are from stdlib, // let's produce a generic diagnostic about invocation and a note // about missing conformance just in case. @@ -2497,9 +2505,6 @@ bool ContextualFailure::diagnoseAsError() { return false; } - if (diagnoseExtraneousAssociatedValues()) - return true; - // Special case of some common conversions involving Swift.String // indexes, catching cases where people attempt to index them with an integer. if (isIntegerToStringIndexConversion()) { @@ -2711,6 +2716,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; } @@ -2923,9 +2941,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; @@ -2940,24 +2960,6 @@ void ContextualFailure::tryFixIts(InFlightDiagnostic &diagnostic) const { return; } -bool ContextualFailure::diagnoseExtraneousAssociatedValues() const { - if (auto match = - getLocator()->getLastElementAs()) { - if (auto enumElementPattern = - dyn_cast(match->getPattern())) { - emitDiagnosticAt(enumElementPattern->getNameLoc(), - diag::enum_element_pattern_assoc_values_mismatch, - enumElementPattern->getName()); - emitDiagnosticAt(enumElementPattern->getNameLoc(), - diag::enum_element_pattern_assoc_values_remove) - .fixItRemove(enumElementPattern->getSubPattern()->getSourceRange()); - return true; - } - } - - return false; -} - bool ContextualFailure::diagnoseCoercionToUnrelatedType() const { auto anchor = getRawAnchor(); auto *coerceExpr = getAsExpr(anchor); @@ -3563,6 +3565,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: @@ -8708,7 +8712,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, @@ -8731,6 +8735,19 @@ bool TupleLabelMismatchWarning::diagnoseAsError() { return true; } +bool AssociatedValueMismatchFailure::diagnoseAsError() { + auto match = getLocator()->castLastElementTo(); + auto *enumElementPattern = dyn_cast(match.getPattern()); + + emitDiagnosticAt(enumElementPattern->getNameLoc(), + diag::enum_element_pattern_assoc_values_mismatch, + enumElementPattern->getName()); + emitDiagnosticAt(enumElementPattern->getNameLoc(), + diag::enum_element_pattern_assoc_values_remove) + .fixItRemove(enumElementPattern->getSubPattern()->getSourceRange()); + return true; +} + bool SwiftToCPointerConversionInInvalidContext::diagnoseAsError() { auto argInfo = getFunctionArgApplyInfo(getLocator()); if (!argInfo) diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 13ef40c608e50..f598d03dea103 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -674,10 +674,6 @@ class ContextualFailure : public FailureDiagnostic { /// Diagnose failed conversion in a `CoerceExpr`. bool diagnoseCoercionToUnrelatedType() const; - /// Diagnose cases where a pattern tried to match associated values but - /// the enum case had none. - bool diagnoseExtraneousAssociatedValues() const; - /// Produce a specialized diagnostic if this is an invalid conversion to Bool. bool diagnoseConversionToBool() const; @@ -2772,6 +2768,15 @@ class TupleLabelMismatchWarning final : public ContextualFailure { bool diagnoseAsError() override; }; +class AssociatedValueMismatchFailure final : public ContextualFailure { +public: + AssociatedValueMismatchFailure(const Solution &solution, Type fromType, + Type toType, ConstraintLocator *locator) + : ContextualFailure(solution, fromType, toType, locator) {} + + bool diagnoseAsError() override; +}; + /// Diagnose situations where Swift -> C pointer implicit conversion /// is attempted on a Swift function instead of one imported from C header. /// diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index f693ee0478b9e..7f6899bf484fc 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -2139,9 +2139,10 @@ IgnoreResultBuilderWithReturnStmts::create(ConstraintSystem &cs, Type builderTy, bool IgnoreUnresolvedPatternVar::diagnose(const Solution &solution, bool asNote) const { - // Not being able to infer the type of a pattern should already have been - // diagnosed on the pattern's initializer or as a structural issue of the AST. - return true; + // An unresolved AnyPatternDecl means there was some issue in the match + // that means we couldn't infer the pattern. We don't have a diagnostic to + // emit here, the failure should be diagnosed by the fix for expression. + return false; } IgnoreUnresolvedPatternVar * @@ -2426,6 +2427,20 @@ bool AllowTupleLabelMismatch::diagnose(const Solution &solution, return warning.diagnose(asNote); } +AllowAssociatedValueMismatch * +AllowAssociatedValueMismatch::create(ConstraintSystem &cs, Type fromType, + Type toType, ConstraintLocator *locator) { + return new (cs.getAllocator()) + AllowAssociatedValueMismatch(cs, fromType, toType, locator); +} + +bool AllowAssociatedValueMismatch::diagnose(const Solution &solution, + bool asNote) const { + AssociatedValueMismatchFailure failure(solution, getFromType(), getToType(), + getLocator()); + return failure.diagnose(asNote); +} + bool AllowSwiftToCPointerConversion::diagnose(const Solution &solution, bool asNote) const { SwiftToCPointerConversionInInvalidContext failure(solution, getLocator()); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index b3422644680fb..867a9f74ba1d6 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); } @@ -2522,16 +2491,17 @@ namespace { : nullptr; }; + auto matchLoc = + 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 { type = CS.createTypeVariable( - CS.getConstraintLocator(pattern, - ConstraintLocator::AnyPatternDecl), + CS.getConstraintLocator(matchLoc, + LocatorPathElt::AnyPatternDecl()), TVO_CanBindToNoEscape | TVO_CanBindToHole); } return setType(type); @@ -2566,9 +2536,12 @@ namespace { } } + auto matchLoc = + locator.withPathElement(LocatorPathElt::PatternMatch(pattern)); + if (!varType) { varType = CS.createTypeVariable( - CS.getConstraintLocator(pattern, + CS.getConstraintLocator(matchLoc, LocatorPathElt::NamedPatternDecl()), TVO_CanBindToNoEscape | TVO_CanBindToHole); @@ -2602,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 @@ -2685,9 +2653,6 @@ namespace { Type type = TypeChecker::typeCheckPattern(contextualPattern); - if (!type) - return Type(); - // Look through reference storage types. type = type->getReferenceStorageReferent(); @@ -2702,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 @@ -2722,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())); } @@ -2765,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)); } @@ -2797,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 @@ -2822,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); } @@ -2858,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 = [&] { @@ -2883,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; } @@ -2930,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); @@ -2951,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"); @@ -4415,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( @@ -4839,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 159460bf4a9f3..d50b00c826f06 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. @@ -2763,24 +2765,17 @@ assessRequirementFailureImpact(ConstraintSystem &cs, Type requirementType, } } - // Increase the impact of a conformance fix for a standard library - // or foundation type, as it's unlikely to be a good suggestion. - // - // Also do the same for the builtin compiler types Any and AnyObject, - // which cannot conform to protocols. - // - // FIXME: We ought not to have the is() condition here, but - // removing it currently regresses the diagnostic for the test case for - // rdar://60727310. Once we better handle the separation of conformance fixes - // from argument mismatches in cases like - // https://github.com/apple/swift/issues/54877, we should be able to remove - // it from the condition. - if ((requirementType->is() && resolvedTy->isStdlibType()) || - resolvedTy->isAny() || resolvedTy->isAnyObject() || - getKnownFoundationEntity(resolvedTy->getString())) { - if (locator.isForRequirement(RequirementKind::Conformance)) { + if (locator.isForRequirement(RequirementKind::Conformance)) { + // Increase the impact of a conformance fix for a standard library + // or foundation type, as it's unlikely to be a good suggestion. + if (resolvedTy->isStdlibType() || + getKnownFoundationEntity(resolvedTy->getString())) { impact += 2; } + // Also do the same for the builtin compiler types Any and AnyObject, but + // bump the impact even higher as they cannot conform to protocols at all. + if (resolvedTy->isAny() || resolvedTy->isAnyObject()) + impact += 4; } // If this requirement is associated with an overload choice let's @@ -3530,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; } @@ -6252,16 +6238,6 @@ bool ConstraintSystem::repairFailures( // `Int` vs. `(_, _)`. recordAnyTypeVarAsPotentialHole(rhs); - // If the element type is `Any` i.e. `for (x, y) in [] { ... }` - // it would never match and the pattern (`rhs` = `(x, y)`) - // doesn't have any other source of contextual information, - // so instead of waiting for elements to become holes with an - // unrelated fixes, let's proactively bind all of the pattern - // elemnts to holes here. - if (lhs->isAny()) { - recordTypeVariablesAsHoles(rhs); - } - conversionsOrFixes.push_back(CollectionElementContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; @@ -6387,8 +6363,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); @@ -6403,14 +6391,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(ContextualMismatch::create( + conversionsOrFixes.push_back(AllowAssociatedValueMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); + break; } // `weak` declaration with an explicit non-optional type e.g. @@ -6423,7 +6409,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))); @@ -6432,6 +6418,9 @@ bool ConstraintSystem::repairFailures( } } + conversionsOrFixes.push_back(ContextualMismatch::create( + *this, lhs, rhs, getConstraintLocator(locator))); + break; } @@ -6815,22 +6804,6 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, } } - // If the left-hand side of a 'sequence element' constraint - // is a dependent member type without any type variables it - // means that conformance check has been "fixed". - // Let's record other side of the conversion as a "hole" - // to give the solver a chance to continue and avoid - // producing diagnostics for both missing conformance and - // invalid element type. - if (shouldAttemptFixes()) { - if (locator.endsWith() && - desugar1->is() && - !desugar1->hasTypeVariable()) { - recordPotentialHole(typeVar2); - return getTypeMatchSuccess(); - } - } - return formUnsolvedResult(); } @@ -14661,6 +14634,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowInvalidPackExpansion: case FixKind::MacroMissingPound: case FixKind::AllowGlobalActorMismatch: + case FixKind::AllowAssociatedValueMismatch: case FixKind::GenericArgumentsMismatch: { return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } @@ -14854,8 +14828,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 d4b026fa2fdcd..41e5cfbceefa0 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 827eab1ba8387..f2d878c1c2702 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 8c0f1c9e2f93d..a03b317595a86 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; @@ -669,6 +674,18 @@ ConstraintLocator::isForSingleValueStmtBranch() const { return SingleValueStmtBranchKind::Regular; } +NullablePtr ConstraintLocator::getPatternMatch() const { + auto matchElt = findLast(); + if (!matchElt) + return nullptr; + + 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 f12ce3465a5f0..10563c1fe10f8 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3926,12 +3926,6 @@ struct TypeSimplifier { auto conformance = CS.DC->getParentModule()->lookupConformance( lookupBaseType, proto); if (!conformance) { - // FIXME: This regresses diagnostics if removed, but really the - // handling of a missing conformance should be the same for - // tuples and non-tuples. - if (lookupBaseType->is()) - return DependentMemberType::get(lookupBaseType, assocType); - // If the base type doesn't conform to the associatedtype's protocol, // there will be a missing conformance fix applied in diagnostic mode, // so the concrete dependent member type is considered a "hole" in @@ -5830,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 9b15982e35b66..ccecf769eebc1 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 d0d8bdbf76a9c..e489f0be83660 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -729,15 +729,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/optional.swift b/test/Constraints/optional.swift index f1b42378c9460..0434771e95ce0 100644 --- a/test/Constraints/optional.swift +++ b/test/Constraints/optional.swift @@ -435,8 +435,7 @@ func test_force_unwrap_not_being_too_eager() { // rdar://problem/57097401 func invalidOptionalChaining(a: Any) { a == "="? // expected-error {{cannot use optional chaining on non-optional value of type 'String'}} - // expected-error@-1 {{type 'Any' cannot conform to 'Equatable'}} - // expected-note@-2 {{requirement from conditional conformance of 'Any?' to 'Equatable'}} expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}} + // expected-error@-1 {{binary operator '==' cannot be applied to operands of type 'Any' and 'String?'}} } /// https://github.com/apple/swift/issues/54739 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/rdar107651291.swift b/test/Constraints/rdar107651291.swift new file mode 100644 index 0000000000000..342c9222621be --- /dev/null +++ b/test/Constraints/rdar107651291.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://107651291 – Make sure we don't crash +func foo(xs: [String: [String]], ys: [String: [String]]) { + for (key, value) in xs { + guard let ys = ys.first(where: { $0.key == key }) else { return } + for (a, b) in zip(xs, ys) {} + // expected-error@-1 {{type 'Dictionary.Element' (aka '(key: String, value: Array)') cannot conform to 'Sequence'}} + // expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}} + // expected-note@-3 {{required by referencing instance method 'next()'}} + } +} 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/rdar107724970.swift b/test/Constraints/rdar107724970.swift new file mode 100644 index 0000000000000..2008a8907b934 --- /dev/null +++ b/test/Constraints/rdar107724970.swift @@ -0,0 +1,17 @@ +// RUN: %target-typecheck-verify-swift + +// rdar://107724970 – Make sure we don't crash. +enum E { + case e(Int) +} +func foo(_ x: E) { + // FIXME: We need to handle pattern arguments in a bunch of places in argument + // list diagnostic logic. + // https://github.com/apple/swift/issues/65062 + let fn = { // expected-error {{unable to infer closure type in the current context}} + switch x { + case E.e(_, _): + 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/rdar44770297.swift b/test/Constraints/rdar44770297.swift index 9853dd1ca2f53..0853b150b353a 100644 --- a/test/Constraints/rdar44770297.swift +++ b/test/Constraints/rdar44770297.swift @@ -8,4 +8,8 @@ func foo(_: () throws -> T) -> T.A? { // expected-note {{where 'T' = 'Neve fatalError() } -let _ = foo() {fatalError()} & nil // expected-error {{global function 'foo' requires that 'Never' conform to 'P'}} +let _ = foo() {fatalError()} & nil +// expected-error@-1 {{global function 'foo' requires that 'Never' conform to 'P'}} +// expected-error@-2 {{value of optional type 'Never.A?' must be unwrapped to a value of type 'Never.A'}} +// expected-note@-3 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} +// expected-note@-4 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} 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/Constraints/static_members_on_protocol_in_generic_context.swift b/test/Constraints/static_members_on_protocol_in_generic_context.swift index fe3da9e02da3c..90a11ab8a321a 100644 --- a/test/Constraints/static_members_on_protocol_in_generic_context.swift +++ b/test/Constraints/static_members_on_protocol_in_generic_context.swift @@ -177,7 +177,7 @@ test_combo(.genericFn(42)) // expected-error {{global function 'test_combo' requ extension P { // expected-note 13 {{missing same-type requirement on 'Self'}} {{12-12= where Self == <#Type#>}} static func generic(_: T) -> T { fatalError() } - static func genericWithReqs(_: T) -> Q where T.Element == Q { // expected-note {{in call to function 'genericWithReqs'}} expected-note {{required by static method 'genericWithReqs' where 'T' = '()'}} + static func genericWithReqs(_: T) -> Q where T.Element == Q { // expected-note {{required by static method 'genericWithReqs' where 'T' = '()'}} fatalError() } } @@ -217,7 +217,6 @@ _ = P.genericWithReqs([42]) _ = P.genericWithReqs(()) // expected-error@-1 {{type '()' cannot conform to 'Collection'}} expected-note@-1 {{only concrete types such as structs, enums and classes can conform to protocols}} // expected-error@-2 {{static member 'genericWithReqs' cannot be used on protocol metatype '(any P).Type'}} -// expected-error@-3 {{generic parameter 'Q' could not be inferred}} _ = P[q: ""] // expected-error@-1 {{static member 'subscript' cannot be used on protocol metatype '(any P).Type'}} _ = P[q: ""].other 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/Misc/misc_diagnostics.swift b/test/Misc/misc_diagnostics.swift index e8ff80eef2970..6d27d411adcc5 100644 --- a/test/Misc/misc_diagnostics.swift +++ b/test/Misc/misc_diagnostics.swift @@ -144,9 +144,6 @@ func test17875634() { func test20770032() { if case let 1...10 = (1, 1) { // expected-warning{{'let' pattern has no effect; sub-pattern didn't bind any variables}} {{11-15=}} // expected-error@-1 {{expression pattern of type 'ClosedRange' cannot match values of type '(Int, Int)'}} - // expected-error@-2 {{type '(Int, Int)' cannot conform to 'Equatable'}} - // expected-note@-3 {{only concrete types such as structs, enums and classes can conform to protocols}} - // expected-note@-4 {{required by operator function '~=' where 'T' = '(Int, Int)'}} } } diff --git a/test/Parse/confusables.swift b/test/Parse/confusables.swift index 9e5365537f407..a7d8179c3e141 100644 --- a/test/Parse/confusables.swift +++ b/test/Parse/confusables.swift @@ -18,12 +18,8 @@ if (true ꝸꝸꝸ false) {} // expected-note {{identifier 'ꝸꝸꝸ' contains // expected-error @+3 {{invalid character in source file}} // expected-error @+2 {{expected ',' separator}} -// expected-error @+1 {{type '(Int, Int)' cannot conform to 'BinaryInteger'}} +// expected-error @+1 {{binary operator '==' cannot be applied to operands of type '(Int, Int)' and 'Int'}} if (5 ‒ 5) == 0 {} // expected-note {{unicode character '‒' (Figure Dash) looks similar to '-' (Hyphen Minus); did you mean to use '-' (Hyphen Minus)?}} {{7-10=-}} -// expected-note @-1 {{operator function '=='}} -// expected-note @-2 {{only concrete types such as structs, enums and classes can conform to protocols}} - -// FIXME(rdar://61028087): The above note should read "required by referencing operator function '==' on 'BinaryInteger' where 'Self' = '(Int, Int)'". // GREEK QUESTION MARK (which looks like a semicolon) print("A"); print("B") 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/StringProcessing/Parse/forward-slash-regex.swift b/test/StringProcessing/Parse/forward-slash-regex.swift index b713c7ec4b80c..22c6a654e6da0 100644 --- a/test/StringProcessing/Parse/forward-slash-regex.swift +++ b/test/StringProcessing/Parse/forward-slash-regex.swift @@ -263,8 +263,7 @@ default: do {} catch /x/ {} // expected-error@-1 {{expression pattern of type 'Regex' cannot match values of type 'any Error'}} -// expected-error@-2 {{binary operator '~=' cannot be applied to two 'any Error' operands}} -// expected-warning@-3 {{'catch' block is unreachable because no errors are thrown in 'do' block}} +// expected-warning@-2 {{'catch' block is unreachable because no errors are thrown in 'do' block}} switch /x/ { 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/test/stmt/foreach.swift b/test/stmt/foreach.swift index 0008d8631ac46..a2eb6af5bf8e7 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -268,3 +268,8 @@ do { for (x, y, z) in [] { // expected-error {{tuple pattern cannot match values of non-tuple type 'Any'}} } } + +do { + // https://github.com/apple/swift/issues/65650 - Make sure we say 'String', not 'Any'. + for (x, y) in [""] {} // expected-error {{tuple pattern cannot match values of non-tuple type 'String'}} +} diff --git a/test/stmt/if_while_var.swift b/test/stmt/if_while_var.swift index b2a354293f2c8..ea5a9e347231e 100644 --- a/test/stmt/if_while_var.swift +++ b/test/stmt/if_while_var.swift @@ -42,8 +42,13 @@ if var nonOptional { nonOptional = nonOptionalStruct(); _ = nonOptional } // exp guard let nonOptional else { _ = nonOptional; fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalStruct'}} guard var nonOptional else { _ = nonOptional; fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalStruct'}} -if let nonOptional.property { } // expected-error{{unwrap condition requires a valid identifier}} expected-error{{pattern matching in a condition requires the 'case' keyword}} -if var nonOptional.property { } // expected-error{{unwrap condition requires a valid identifier}} expected-error{{pattern matching in a condition requires the 'case' keyword}} +if let nonOptional.property { } +// expected-error@-1 {{unwrap condition requires a valid identifier}} +// expected-error@-2 {{initializer for conditional binding must have Optional type, not 'Any'}} + +if var nonOptional.property { } +// expected-error@-1 {{unwrap condition requires a valid identifier}} +// expected-error@-2 {{initializer for conditional binding must have Optional type, not 'Any'}} guard let _ = nonOptionalStruct() else { fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalStruct'}} guard let _ = nonOptionalEnum() else { fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalEnum'}} @@ -65,7 +70,11 @@ class B {} // expected-note * {{did you mean 'B'?}} class D : B {}// expected-note * {{did you mean 'D'?}} // TODO poor recovery in these cases -if let {} // expected-error {{expected '{' after 'if' condition}} expected-error {{pattern matching in a condition requires the 'case' keyword}} expected-error {{unwrap condition requires a valid identifier}} +if let {} +// expected-error@-1 {{expected '{' after 'if' condition}} +// expected-error@-2 {{unwrap condition requires a valid identifier}} +// expected-error@-3 {{initializer for conditional binding must have Optional type, not '() -> ()'}} + if let x = { } // expected-error{{'{' after 'if'}} expected-error{{initializer for conditional binding must have Optional type, not '() -> ()'}} // expected-warning@-1{{value 'x' was defined but never used}} 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 559c350edc851..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,14 +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 {{conflicting arguments to generic parameter 'Elements' ('[Any]' vs. '[Any?]')}} - // expected-error@-2 {{conflicting arguments to generic parameter 'Element' ('Any' vs. 'Any?')}} - // expected-error@-3 {{conflicting arguments to generic parameter 'Self' ('[Any]' vs. '[Any?]')}} +} + +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] {} }