From c853d81d37fd3214d5f6530ce1b53c61f8410ef1 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 19 Oct 2025 19:41:34 +0100 Subject: [PATCH 1/2] [CS] Always eagerly bind member type var to hole for missing member We know this is where the issue is so we ought to always produce a concrete hole. --- lib/Sema/CSFix.cpp | 6 +++++- lib/Sema/CSSimplify.cpp | 19 ++++--------------- test/Constraints/closures.swift | 2 +- test/type/protocol_composition.swift | 8 +++----- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 7f7be538bd14e..237d04c0fab2c 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -939,7 +939,11 @@ DefineMemberBasedOnUse::diagnoseForAmbiguity(CommonFixesArray commonFixes) const const auto *solution = solutionAndFix.first; const auto *fix = solutionAndFix.second->getAs(); - auto baseType = solution->simplifyType(fix->BaseType); + // Ignore differences in optionality since we can look through an optional + // to resolve an implicit member `.foo`. + auto baseType = solution->simplifyType(fix->BaseType) + ->getMetatypeInstanceType() + ->lookThroughAllOptionalTypes(); if (!concreteBaseType) concreteBaseType = baseType; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 21797bc5454b7..6fb0e455c4fe9 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -11604,22 +11604,11 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( // `key path` constraint can't be retired until all components // are simplified. addTypeVariableConstraintsToWorkList(memberTypeVar); - } else if (isa(locator->getAnchor()) && - !getSemanticsProvidingParentExpr( - getAsExpr(locator->getAnchor()))) { - // If there are no contextual expressions that could provide - // a type for the member type variable, let's default it to - // a placeholder eagerly so it could be propagated to the - // pattern if necessary. - recordTypeVariablesAsHoles(memberTypeVar); - } else if (locator->isLastElement()) { - // Let's handle member patterns specifically because they use - // equality instead of argument application constraint, so allowing - // them to bind member could mean missing valid hole positions in - // the pattern. - recordTypeVariablesAsHoles(memberTypeVar); } else { - recordPotentialHole(memberTypeVar); + // Eagerly turn the member type variable into a hole since we know + // this is where the issue is and we've recorded a fix for it. This + // avoids producing unnecessary holes for e.g generic parameters. + recordTypeVariablesAsHoles(memberTypeVar); } } diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 941317f3ac956..7f2e6020001cf 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -1362,7 +1362,7 @@ do { } } - test { // expected-error {{invalid conversion from throwing function of type '(Int) throws -> Void' to non-throwing function type '(Int) -> Void'}} + test { // expected-error {{invalid conversion from throwing function of type '(Int) throws -> _' to non-throwing function type '(Int) -> Void'}} try $0.missing // expected-error {{value of type 'Int' has no member 'missing'}} } } diff --git a/test/type/protocol_composition.swift b/test/type/protocol_composition.swift index f138c2681decb..9fb93e79e58ac 100644 --- a/test/type/protocol_composition.swift +++ b/test/type/protocol_composition.swift @@ -196,11 +196,9 @@ takesP1AndP2([DoesNotExist & P1 & P2]()) // expected-error {{cannot find 'DoesNo // expected-error@-2 {{binary operator '&' cannot be applied to operands of type 'UInt8' and '(any P1).Type'}} // expected-note@-3 2 {{overloads for '&' exist with these partially matching parameter lists}} // expected-error@-4 {{cannot call value of non-function type '[UInt8]'}} -takesP1AndP2([Swift.DoesNotExist & P1 & P2]()) // expected-error {{module 'Swift' has no member named 'DoesNotExist'}} -// expected-error@-1 {{binary operator '&' cannot be applied to operands of type 'UInt8' and '(any P2).Type'}} -// expected-error@-2 {{binary operator '&' cannot be applied to operands of type 'UInt8' and '(any P1).Type'}} -// expected-note@-3 2 {{overloads for '&' exist with these partially matching parameter lists}} -// expected-error@-4 {{cannot call value of non-function type '[UInt8]'}} +takesP1AndP2([Swift.DoesNotExist & P1 & P2]()) +// expected-error@-1 {{module 'Swift' has no member named 'DoesNotExist'}} +// expected-error@-2 {{cannot call value of non-function type '[Element]'}} typealias T08 = P1 & inout P2 // expected-error {{'inout' may only be used on parameters}} typealias T09 = P1 & __shared P2 // expected-error {{'__shared' may only be used on parameters}} From 61a5ae8e01cf01aa2e913b3562a439b9895c1f89 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 27 Oct 2025 20:59:10 +0000 Subject: [PATCH 2/2] [CS] Eagerly bind hole in `recordInvalidNode` We know this is where the issue is so we can immediately bind to a hole, ensuring we don't produce unnecessary downstream diagnostics from things we can't infer. --- lib/Sema/CSGen.cpp | 11 +++++++---- lib/Sema/CSSimplify.cpp | 4 ++++ lib/Sema/ConstraintSystem.cpp | 6 +++--- test/Constraints/diagnostics.swift | 6 ++++-- test/Constraints/ternary_expr.swift | 4 ++++ test/Parse/subscripting.swift | 1 - test/stmt/statements.swift | 3 +++ test/type/protocol_composition.swift | 8 +++----- .../ConnectedComponents-unionSets-486a70.swift | 2 +- ...raintGraph-computeConnectedComponents-57c387.swift | 2 +- .../ConstraintWalker-walkToExprPost-c956e0.swift | 2 +- .../ExprRewriter-visitDeclRefExpr-3b6836.swift | 2 +- .../PackType-getExpandedGenericArgs-7c107d.swift | 2 +- 13 files changed, 33 insertions(+), 20 deletions(-) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/ConnectedComponents-unionSets-486a70.swift (81%) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/ConstraintGraph-computeConnectedComponents-57c387.swift (84%) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/ConstraintWalker-walkToExprPost-c956e0.swift (79%) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/ExprRewriter-visitDeclRefExpr-3b6836.swift (81%) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/PackType-getExpandedGenericArgs-7c107d.swift (84%) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 4653856e441ed..30abd5403f3c9 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1308,14 +1308,17 @@ namespace { CS.setType(expr, expansionType); } - /// Records a fix for an invalid AST node, and returns a potential hole - /// type variable for it. + /// Records a fix for an invalid AST node, and returns a hole for it. Type recordInvalidNode(ASTNode node) { CS.recordFix( IgnoreInvalidASTNode::create(CS, CS.getConstraintLocator(node))); - return CS.createTypeVariable(CS.getConstraintLocator(node), - TVO_CanBindToHole); + // Ideally we wouldn't need a type variable here, but we don't have a + // suitable placeholder originator for all the cases here. + auto ty = CS.createTypeVariable(CS.getConstraintLocator(node), + TVO_CanBindToHole); + CS.recordTypeVariablesAsHoles(ty); + return ty; } virtual Type visitErrorExpr(ErrorExpr *E) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 6fb0e455c4fe9..3d65c36ccec1d 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6694,6 +6694,10 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::Condition: { + // If the condition is already a hole, we're done. + if (lhs->isPlaceholder()) + return true; + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) return true; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 9701c4005add6..5a86c9b62733a 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3581,12 +3581,13 @@ void constraints::simplifyLocator(ASTNode &anchor, } case ConstraintLocator::AutoclosureResult: case ConstraintLocator::LValueConversion: + case ConstraintLocator::OptionalInjection: case ConstraintLocator::DynamicType: case ConstraintLocator::UnresolvedMember: case ConstraintLocator::ImplicitCallAsFunction: // Arguments in autoclosure positions, lvalue and rvalue adjustments, - // unresolved members, and implicit callAsFunction references are - // implicit. + // optional injections, unresolved members, and implicit callAsFunction + // references are implicit. path = path.slice(1); continue; @@ -3913,7 +3914,6 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::Witness: case ConstraintLocator::WrappedValue: - case ConstraintLocator::OptionalInjection: case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: case ConstraintLocator::FallbackType: case ConstraintLocator::KeyPathSubscriptIndex: diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 2f20663e5a060..ecb02f9472054 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -1126,10 +1126,12 @@ func rdar17170728() { // expected-error@-1 4 {{optional type 'Int?' cannot be used as a boolean; test for '!= nil' instead}} } + // FIXME: Bad diagnostic, `Bool.Stride` is bogus, we shouldn't be suggesting + // `reduce(into:)`, and the actual problem is that Int cannot be used as a boolean + // condition. let _ = [i, j, k].reduce(0 as Int?) { // expected-error {{missing argument label 'into:' in call}} - // expected-error@-1 {{cannot convert value of type 'Int?' to expected argument type '(inout (Bool, Bool) -> Bool?, Int?) throws -> ()'}} $0 && $1 ? $0 + $1 : ($0 ? $0 : ($1 ? $1 : nil)) - // expected-error@-1 {{binary operator '+' cannot be applied to two 'Bool' operands}} + // expected-error@-1 {{binary operator '+' cannot be applied to operands of type 'Bool.Stride' and 'Bool'}} } } diff --git a/test/Constraints/ternary_expr.swift b/test/Constraints/ternary_expr.swift index 568840c18b4bd..f81aceb1868a6 100644 --- a/test/Constraints/ternary_expr.swift +++ b/test/Constraints/ternary_expr.swift @@ -59,6 +59,10 @@ _ = true ? x : 1.2 // expected-error {{result values in '? :' expression have mi _ = (x: true) ? true : false // expected-error {{cannot convert value of type '(x: Bool)' to expected condition type 'Bool'}} _ = (x: 1) ? true : false // expected-error {{cannot convert value of type '(x: Int)' to expected condition type 'Bool'}} +_ = undefined ? 0 : 1 // expected-error {{cannot find 'undefined' in scope}} +_ = [undefined] ? 0 : 1 // expected-error {{cannot find 'undefined' in scope}} +// expected-error@-1 {{cannot convert value of type '[Element]' to expected condition type 'Bool'}} + func resultBool() -> Bool { true } _ = resultBool ? true : false // expected-error {{function 'resultBool' was used as a property; add () to call it}} {{15-15=()}} diff --git a/test/Parse/subscripting.swift b/test/Parse/subscripting.swift index 93eece26ed7e7..9109ccafef6f7 100644 --- a/test/Parse/subscripting.swift +++ b/test/Parse/subscripting.swift @@ -179,7 +179,6 @@ struct A6 { // expected-note@-2 {{did you mean}} get { return i + j // expected-error {{cannot find 'j' in scope}} - // expected-error@-1 {{cannot convert return expression of type 'Int' to return type '(Int) -> Int'}} } } } diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index 3fde8002744d1..566599474bc91 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -560,6 +560,9 @@ func bad_if() { if (x: false) {} // expected-error {{cannot convert value of type '(x: Bool)' to expected condition type 'Bool'}} if (x: 1) {} // expected-error {{cannot convert value of type '(x: Int)' to expected condition type 'Bool'}} if nil {} // expected-error {{'nil' is not compatible with expected condition type 'Bool'}} + if undefined {} // expected-error {{cannot find 'undefined' in scope}} + if [undefined] {} // expected-error {{cannot find 'undefined' in scope}} + // expected-error@-1 {{cannot convert value of type '[Element]' to expected condition type 'Bool'}} } // Typo correction for loop labels diff --git a/test/type/protocol_composition.swift b/test/type/protocol_composition.swift index 9fb93e79e58ac..39e6d00a4e83f 100644 --- a/test/type/protocol_composition.swift +++ b/test/type/protocol_composition.swift @@ -191,11 +191,9 @@ takesP1AndP2([AnyObject & P1 & P2]()) takesP1AndP2([Swift.AnyObject & P1 & P2]()) takesP1AndP2([AnyObject & protocol_composition.P1 & P2]()) takesP1AndP2([AnyObject & P1 & protocol_composition.P2]()) -takesP1AndP2([DoesNotExist & P1 & P2]()) // expected-error {{cannot find 'DoesNotExist' in scope}} -// expected-error@-1 {{binary operator '&' cannot be applied to operands of type 'UInt8' and '(any P2).Type'}} -// expected-error@-2 {{binary operator '&' cannot be applied to operands of type 'UInt8' and '(any P1).Type'}} -// expected-note@-3 2 {{overloads for '&' exist with these partially matching parameter lists}} -// expected-error@-4 {{cannot call value of non-function type '[UInt8]'}} +takesP1AndP2([DoesNotExist & P1 & P2]()) +// expected-error@-1 {{cannot find 'DoesNotExist' in scope}} +// expected-error@-2 {{cannot call value of non-function type '[Element]'}} takesP1AndP2([Swift.DoesNotExist & P1 & P2]()) // expected-error@-1 {{module 'Swift' has no member named 'DoesNotExist'}} // expected-error@-2 {{cannot call value of non-function type '[Element]'}} diff --git a/validation-test/compiler_crashers/ConnectedComponents-unionSets-486a70.swift b/validation-test/compiler_crashers_fixed/ConnectedComponents-unionSets-486a70.swift similarity index 81% rename from validation-test/compiler_crashers/ConnectedComponents-unionSets-486a70.swift rename to validation-test/compiler_crashers_fixed/ConnectedComponents-unionSets-486a70.swift index f5c619d49e980..6f8f3f5a3c94c 100644 --- a/validation-test/compiler_crashers/ConnectedComponents-unionSets-486a70.swift +++ b/validation-test/compiler_crashers_fixed/ConnectedComponents-unionSets-486a70.swift @@ -1,4 +1,4 @@ // {"kind":"typecheck","signature":"(anonymous namespace)::ConnectedComponents::unionSets(swift::TypeVariableType*, swift::TypeVariableType*)","signatureAssert":"Assertion failed: (validComponentCount > 0), function unionSets"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s let b = ( c , d a { e b diff --git a/validation-test/compiler_crashers/ConstraintGraph-computeConnectedComponents-57c387.swift b/validation-test/compiler_crashers_fixed/ConstraintGraph-computeConnectedComponents-57c387.swift similarity index 84% rename from validation-test/compiler_crashers/ConstraintGraph-computeConnectedComponents-57c387.swift rename to validation-test/compiler_crashers_fixed/ConstraintGraph-computeConnectedComponents-57c387.swift index 78c9c193bdff4..4f8b4244fea60 100644 --- a/validation-test/compiler_crashers/ConstraintGraph-computeConnectedComponents-57c387.swift +++ b/validation-test/compiler_crashers_fixed/ConstraintGraph-computeConnectedComponents-57c387.swift @@ -1,4 +1,4 @@ // {"kind":"typecheck","signature":"swift::constraints::ConstraintGraph::computeConnectedComponents(llvm::ArrayRef)","signatureAssert":"Assertion failed: (component != components.end()), function operator()"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s let i = Array(... a { " ? \(i Array* )" } , b 1 diff --git a/validation-test/compiler_crashers/ConstraintWalker-walkToExprPost-c956e0.swift b/validation-test/compiler_crashers_fixed/ConstraintWalker-walkToExprPost-c956e0.swift similarity index 79% rename from validation-test/compiler_crashers/ConstraintWalker-walkToExprPost-c956e0.swift rename to validation-test/compiler_crashers_fixed/ConstraintWalker-walkToExprPost-c956e0.swift index 3c65a22d943f8..853ee5d6fab7f 100644 --- a/validation-test/compiler_crashers/ConstraintWalker-walkToExprPost-c956e0.swift +++ b/validation-test/compiler_crashers_fixed/ConstraintWalker-walkToExprPost-c956e0.swift @@ -1,5 +1,5 @@ // {"kind":"typecheck","original":"1562769e","signature":"(anonymous namespace)::ConstraintWalker::walkToExprPost(swift::Expr*)"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s enum c func d() e { if let diff --git a/validation-test/compiler_crashers/ExprRewriter-visitDeclRefExpr-3b6836.swift b/validation-test/compiler_crashers_fixed/ExprRewriter-visitDeclRefExpr-3b6836.swift similarity index 81% rename from validation-test/compiler_crashers/ExprRewriter-visitDeclRefExpr-3b6836.swift rename to validation-test/compiler_crashers_fixed/ExprRewriter-visitDeclRefExpr-3b6836.swift index bfc3392b28aec..cbe08cf3c2454 100644 --- a/validation-test/compiler_crashers/ExprRewriter-visitDeclRefExpr-3b6836.swift +++ b/validation-test/compiler_crashers_fixed/ExprRewriter-visitDeclRefExpr-3b6836.swift @@ -1,5 +1,5 @@ // {"kind":"typecheck","signature":"(anonymous namespace)::ExprRewriter::visitDeclRefExpr(swift::DeclRefExpr*)"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s { for b 0 ..< 10 { let a = Array(0 ..< b) diff --git a/validation-test/compiler_crashers/PackType-getExpandedGenericArgs-7c107d.swift b/validation-test/compiler_crashers_fixed/PackType-getExpandedGenericArgs-7c107d.swift similarity index 84% rename from validation-test/compiler_crashers/PackType-getExpandedGenericArgs-7c107d.swift rename to validation-test/compiler_crashers_fixed/PackType-getExpandedGenericArgs-7c107d.swift index adffec2d09067..0c19c9a69112b 100644 --- a/validation-test/compiler_crashers/PackType-getExpandedGenericArgs-7c107d.swift +++ b/validation-test/compiler_crashers_fixed/PackType-getExpandedGenericArgs-7c107d.swift @@ -1,3 +1,3 @@ // {"kind":"typecheck","signature":"swift::PackType::getExpandedGenericArgs(llvm::ArrayRef, llvm::ArrayRef)","signatureAssert":"Assertion failed: (isa(Val) && \"cast() argument of incompatible type!\"), function cast"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s struct a < each b typealias c = a<> c < e