[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.
This commit is contained in:
Hamish Knight
2025-10-27 20:59:10 +00:00
parent c853d81d37
commit 61a5ae8e01
13 changed files with 33 additions and 20 deletions

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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:

View File

@@ -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'}}
}
}

View File

@@ -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=()}}

View File

@@ -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'}}
}
}
}

View File

@@ -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

View File

@@ -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]'}}

View File

@@ -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

View File

@@ -1,4 +1,4 @@
// {"kind":"typecheck","signature":"swift::constraints::ConstraintGraph::computeConnectedComponents(llvm::ArrayRef<swift::TypeVariableType*>)","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

View File

@@ -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

View File

@@ -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)

View File

@@ -1,3 +1,3 @@
// {"kind":"typecheck","signature":"swift::PackType::getExpandedGenericArgs(llvm::ArrayRef<swift::GenericTypeParamType*>, llvm::ArrayRef<swift::Type>)","signatureAssert":"Assertion failed: (isa<To>(Val) && \"cast<Ty>() 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<each d> = a<> c < e