Skip to content

Commit 2877223

Browse files
committed
[CS] Allow contextual types with errors
Previously we would skip type-checking the result expression of a `return` or the initialization expression of a binding if the contextual type had an error, but that misses out on useful diagnostics and prevents code completion and cursor info from working. Change the logic such that we open ErrorTypes as holes and continue to type-check.
1 parent 1b0eed2 commit 2877223

File tree

13 files changed

+64
-28
lines changed

13 files changed

+64
-28
lines changed

lib/Sema/CSGen.cpp

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4913,16 +4913,27 @@ bool ConstraintSystem::generateConstraints(
49134913
return convertTypeLocator;
49144914
};
49154915

4916-
// Substitute type variables in for placeholder types.
4917-
convertType = convertType.transformRec([&](Type type) -> std::optional<Type> {
4918-
if (type->is<PlaceholderType>()) {
4919-
return Type(createTypeVariable(getLocator(type),
4920-
TVO_CanBindToNoEscape |
4921-
TVO_PrefersSubtypeBinding |
4922-
TVO_CanBindToHole));
4923-
}
4924-
return std::nullopt;
4925-
});
4916+
// If the contextual type has an error, we can't apply the solution.
4917+
// Record a fix for an invalid AST node.
4918+
if (convertType->hasError())
4919+
recordFix(IgnoreInvalidASTNode::create(*this, convertTypeLocator));
4920+
4921+
// Substitute type variables in for placeholder and error types.
4922+
convertType =
4923+
convertType.transformRec([&](Type type) -> std::optional<Type> {
4924+
if (!isa<PlaceholderType, ErrorType>(type.getPointer()))
4925+
return std::nullopt;
4926+
4927+
auto flags = TVO_CanBindToNoEscape | TVO_PrefersSubtypeBinding |
4928+
TVO_CanBindToHole;
4929+
auto tv = Type(createTypeVariable(getLocator(type), flags));
4930+
// For ErrorTypes we want to eagerly bind to a hole since we
4931+
// know this is where the issue is.
4932+
if (isa<ErrorType>(type.getPointer())) {
4933+
recordTypeVariablesAsHoles(tv);
4934+
}
4935+
return tv;
4936+
});
49264937

49274938
addContextualConversionConstraint(expr, convertType, ctp,
49284939
convertTypeLocator);

lib/Sema/CSSyntacticElement.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -845,12 +845,6 @@ class SyntacticElementConstraintGenerator
845845
ContextualPattern::forPatternBindingDecl(patternBinding, index);
846846
Type patternType = TypeChecker::typeCheckPattern(contextualPattern);
847847

848-
// Fail early if pattern couldn't be type-checked.
849-
if (!patternType || patternType->hasError()) {
850-
hadError = true;
851-
return;
852-
}
853-
854848
auto target = getTargetForPattern(patternBinding, index, patternType);
855849
if (!target) {
856850
hadError = true;

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -876,11 +876,6 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
876876
auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC);
877877
patternType = typeCheckPattern(contextualPattern);
878878
}
879-
880-
if (patternType->hasError()) {
881-
PBD->setInvalid();
882-
return true;
883-
}
884879
}
885880

886881
bool hadError = TypeChecker::typeCheckBinding(pattern, init, DC, patternType,

lib/Sema/TypeCheckStmt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
10751075
assert(TheFunc && "Should have bailed from pre-check if this is None");
10761076

10771077
Type ResultTy = TheFunc->getBodyResultType();
1078-
if (!ResultTy || ResultTy->hasError())
1078+
if (!ResultTy)
10791079
return nullptr;
10801080

10811081
if (!RS->hasResult()) {

lib/Sema/TypeOfReference.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -273,16 +273,32 @@ class InferableTypeOpener final {
273273
return substTy;
274274
}
275275

276+
Type transformErrorType(ErrorType *errTy) {
277+
// For ErrorTypes we want to eagerly bind to a hole since we know this is
278+
// where the issue is.
279+
auto *tv = createTypeVariable(cs.getConstraintLocator(locator));
280+
cs.recordTypeVariablesAsHoles(tv);
281+
return tv;
282+
}
283+
276284
Type transform(Type type) {
277285
if (!type)
278286
return type;
279-
if (!type->hasUnboundGenericType() && !type->hasPlaceholder())
287+
if (!type->hasUnboundGenericType() && !type->hasPlaceholder() &&
288+
!type->hasError()) {
280289
return type;
281-
290+
}
291+
// If the type has an error, we can't apply the solution. Record a fix for
292+
// an invalid AST node.
293+
if (type->hasError()) {
294+
cs.recordFix(
295+
IgnoreInvalidASTNode::create(cs, cs.getConstraintLocator(locator)));
296+
}
282297
return type.transformRec([&](Type type) -> std::optional<Type> {
283-
if (!type->hasUnboundGenericType() && !type->hasPlaceholder())
298+
if (!type->hasUnboundGenericType() && !type->hasPlaceholder() &&
299+
!type->hasError()) {
284300
return type;
285-
301+
}
286302
auto *tyPtr = type.getPointer();
287303
if (auto unbound = dyn_cast<UnboundGenericType>(tyPtr))
288304
return transformUnboundGenericType(unbound);
@@ -292,6 +308,8 @@ class InferableTypeOpener final {
292308
return transformBoundGenericType(genericTy);
293309
if (auto *aliasTy = dyn_cast<TypeAliasType>(tyPtr))
294310
return transformTypeAliasType(aliasTy);
311+
if (auto *errTy = dyn_cast<ErrorType>(tyPtr))
312+
return transformErrorType(errTy);
295313

296314
return std::nullopt;
297315
});

test/ClangImporter/overlay.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ Redeclaration.NSStringToNSString(AppKit.NSStringToNSString("abc")) // expected-w
1919

2020
let viaStruct: Redeclaration.FooStruct1 = AppKit.FooStruct1()
2121
let forwardDecl: Redeclaration.Tribool = AppKit.Tribool() // expected-error {{no type named 'Tribool' in module 'Redeclaration'}}
22+
// expected-error@-1 {{missing argument for parameter #1 in call}}

test/Constraints/invalid_constraint_lookup.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ protocol Collection : _Collection, Sequence {
2727
}
2828
func insertionSort<C: Mutable> (_ elements: inout C, i: C.Index) { // expected-error {{cannot find type 'Mutable' in scope}} expected-error {{'Index' is not a member type of type 'C'}}
2929
var x: C.Iterator.Element = elements[i] // expected-error {{'Iterator' is not a member type of type 'C'}}
30+
// expected-error@-1 {{value of type 'C' has no subscripts}}
3031
}

test/Constraints/patterns.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,3 +834,10 @@ func testUndefinedTypeInPattern(_ x: Int) {
834834
}
835835
}
836836
}
837+
838+
func testUndefinedInClosureVar() {
839+
// Make sure we don't produce "unable to infer closure type without a type annotation"
840+
_ = {
841+
var x: Undefined // expected-error {{cannot find type 'Undefined' in scope}}
842+
}
843+
}

test/Parse/subscripting.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ struct A2 {
142142
subscript (i : Int) -> // expected-error{{expected subscripting element type}}
143143
{
144144
get {
145-
return stored
145+
return stored // expected-error {{cannot find 'stored' in scope}}
146146
}
147147
set {
148148
stored = newValue // expected-error{{cannot find 'stored' in scope}}

test/SPI/implicit_spi_import.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ import Lib
9595
public func useImplicit() -> _Klass { return _Klass() } // expected-error{{cannot use class '_Klass' here; it is an SPI imported from 'Lib'}}
9696

9797
@_spi(core)
98-
public func useSPICore() -> CoreStruct { return CoreStruct() } // expected-error{{cannot find type 'CoreStruct' in scope}}
98+
public func useSPICore() -> CoreStruct { return CoreStruct() }
99+
// expected-error@-1 {{cannot find type 'CoreStruct' in scope}}
100+
// expected-error@-2 {{cannot find 'CoreStruct' in scope}}
99101

100102
public func useMain() -> APIProtocol? { return nil }
101103

0 commit comments

Comments
 (0)