mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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.
This commit is contained in:
@@ -4913,16 +4913,27 @@ bool ConstraintSystem::generateConstraints(
|
||||
return convertTypeLocator;
|
||||
};
|
||||
|
||||
// Substitute type variables in for placeholder types.
|
||||
convertType = convertType.transformRec([&](Type type) -> std::optional<Type> {
|
||||
if (type->is<PlaceholderType>()) {
|
||||
return Type(createTypeVariable(getLocator(type),
|
||||
TVO_CanBindToNoEscape |
|
||||
TVO_PrefersSubtypeBinding |
|
||||
TVO_CanBindToHole));
|
||||
}
|
||||
return std::nullopt;
|
||||
});
|
||||
// If the contextual type has an error, we can't apply the solution.
|
||||
// Record a fix for an invalid AST node.
|
||||
if (convertType->hasError())
|
||||
recordFix(IgnoreInvalidASTNode::create(*this, convertTypeLocator));
|
||||
|
||||
// Substitute type variables in for placeholder and error types.
|
||||
convertType =
|
||||
convertType.transformRec([&](Type type) -> std::optional<Type> {
|
||||
if (!isa<PlaceholderType, ErrorType>(type.getPointer()))
|
||||
return std::nullopt;
|
||||
|
||||
auto flags = TVO_CanBindToNoEscape | TVO_PrefersSubtypeBinding |
|
||||
TVO_CanBindToHole;
|
||||
auto tv = Type(createTypeVariable(getLocator(type), flags));
|
||||
// For ErrorTypes we want to eagerly bind to a hole since we
|
||||
// know this is where the issue is.
|
||||
if (isa<ErrorType>(type.getPointer())) {
|
||||
recordTypeVariablesAsHoles(tv);
|
||||
}
|
||||
return tv;
|
||||
});
|
||||
|
||||
addContextualConversionConstraint(expr, convertType, ctp,
|
||||
convertTypeLocator);
|
||||
|
||||
@@ -845,12 +845,6 @@ private:
|
||||
ContextualPattern::forPatternBindingDecl(patternBinding, index);
|
||||
Type patternType = TypeChecker::typeCheckPattern(contextualPattern);
|
||||
|
||||
// Fail early if pattern couldn't be type-checked.
|
||||
if (!patternType || patternType->hasError()) {
|
||||
hadError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
auto target = getTargetForPattern(patternBinding, index, patternType);
|
||||
if (!target) {
|
||||
hadError = true;
|
||||
|
||||
@@ -876,11 +876,6 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
|
||||
auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC);
|
||||
patternType = typeCheckPattern(contextualPattern);
|
||||
}
|
||||
|
||||
if (patternType->hasError()) {
|
||||
PBD->setInvalid();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool hadError = TypeChecker::typeCheckBinding(pattern, init, DC, patternType,
|
||||
|
||||
@@ -1075,7 +1075,7 @@ public:
|
||||
assert(TheFunc && "Should have bailed from pre-check if this is None");
|
||||
|
||||
Type ResultTy = TheFunc->getBodyResultType();
|
||||
if (!ResultTy || ResultTy->hasError())
|
||||
if (!ResultTy)
|
||||
return nullptr;
|
||||
|
||||
if (!RS->hasResult()) {
|
||||
|
||||
@@ -273,16 +273,32 @@ public:
|
||||
return substTy;
|
||||
}
|
||||
|
||||
Type transformErrorType(ErrorType *errTy) {
|
||||
// For ErrorTypes we want to eagerly bind to a hole since we know this is
|
||||
// where the issue is.
|
||||
auto *tv = createTypeVariable(cs.getConstraintLocator(locator));
|
||||
cs.recordTypeVariablesAsHoles(tv);
|
||||
return tv;
|
||||
}
|
||||
|
||||
Type transform(Type type) {
|
||||
if (!type)
|
||||
return type;
|
||||
if (!type->hasUnboundGenericType() && !type->hasPlaceholder())
|
||||
if (!type->hasUnboundGenericType() && !type->hasPlaceholder() &&
|
||||
!type->hasError()) {
|
||||
return type;
|
||||
|
||||
}
|
||||
// If the type has an error, we can't apply the solution. Record a fix for
|
||||
// an invalid AST node.
|
||||
if (type->hasError()) {
|
||||
cs.recordFix(
|
||||
IgnoreInvalidASTNode::create(cs, cs.getConstraintLocator(locator)));
|
||||
}
|
||||
return type.transformRec([&](Type type) -> std::optional<Type> {
|
||||
if (!type->hasUnboundGenericType() && !type->hasPlaceholder())
|
||||
if (!type->hasUnboundGenericType() && !type->hasPlaceholder() &&
|
||||
!type->hasError()) {
|
||||
return type;
|
||||
|
||||
}
|
||||
auto *tyPtr = type.getPointer();
|
||||
if (auto unbound = dyn_cast<UnboundGenericType>(tyPtr))
|
||||
return transformUnboundGenericType(unbound);
|
||||
@@ -292,6 +308,8 @@ public:
|
||||
return transformBoundGenericType(genericTy);
|
||||
if (auto *aliasTy = dyn_cast<TypeAliasType>(tyPtr))
|
||||
return transformTypeAliasType(aliasTy);
|
||||
if (auto *errTy = dyn_cast<ErrorType>(tyPtr))
|
||||
return transformErrorType(errTy);
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
@@ -19,3 +19,4 @@ Redeclaration.NSStringToNSString(AppKit.NSStringToNSString("abc")) // expected-w
|
||||
|
||||
let viaStruct: Redeclaration.FooStruct1 = AppKit.FooStruct1()
|
||||
let forwardDecl: Redeclaration.Tribool = AppKit.Tribool() // expected-error {{no type named 'Tribool' in module 'Redeclaration'}}
|
||||
// expected-error@-1 {{missing argument for parameter #1 in call}}
|
||||
|
||||
@@ -27,4 +27,5 @@ protocol Collection : _Collection, Sequence {
|
||||
}
|
||||
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'}}
|
||||
var x: C.Iterator.Element = elements[i] // expected-error {{'Iterator' is not a member type of type 'C'}}
|
||||
// expected-error@-1 {{value of type 'C' has no subscripts}}
|
||||
}
|
||||
|
||||
@@ -834,3 +834,10 @@ func testUndefinedTypeInPattern(_ x: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUndefinedInClosureVar() {
|
||||
// Make sure we don't produce "unable to infer closure type without a type annotation"
|
||||
_ = {
|
||||
var x: Undefined // expected-error {{cannot find type 'Undefined' in scope}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ struct A2 {
|
||||
subscript (i : Int) -> // expected-error{{expected subscripting element type}}
|
||||
{
|
||||
get {
|
||||
return stored
|
||||
return stored // expected-error {{cannot find 'stored' in scope}}
|
||||
}
|
||||
set {
|
||||
stored = newValue // expected-error{{cannot find 'stored' in scope}}
|
||||
|
||||
@@ -95,7 +95,9 @@ import Lib
|
||||
public func useImplicit() -> _Klass { return _Klass() } // expected-error{{cannot use class '_Klass' here; it is an SPI imported from 'Lib'}}
|
||||
|
||||
@_spi(core)
|
||||
public func useSPICore() -> CoreStruct { return CoreStruct() } // expected-error{{cannot find type 'CoreStruct' in scope}}
|
||||
public func useSPICore() -> CoreStruct { return CoreStruct() }
|
||||
// expected-error@-1 {{cannot find type 'CoreStruct' in scope}}
|
||||
// expected-error@-2 {{cannot find 'CoreStruct' in scope}}
|
||||
|
||||
public func useMain() -> APIProtocol? { return nil }
|
||||
|
||||
|
||||
@@ -257,6 +257,7 @@ func mismatchedReturnTypes() -> _ { // expected-error {{type placeholder may not
|
||||
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
|
||||
func opaque() -> some _ { // expected-error {{type placeholder not allowed here}}
|
||||
return Just<Int>().setFailureType(to: _.self)
|
||||
// expected-error@-1 {{type placeholder not allowed here}}
|
||||
}
|
||||
|
||||
enum EnumWithPlaceholders {
|
||||
|
||||
@@ -126,7 +126,12 @@ func test21057425() -> (Int, Int) {
|
||||
// rdar://problem/21081340
|
||||
func test21081340() {
|
||||
func foo() { }
|
||||
|
||||
// FIXME: The double diagnostic here is a little unfortunate but is a result of
|
||||
// the fact that we essentially type-check the pattern twice, once on its own,
|
||||
// and again as part of the initialization.
|
||||
let (x: a, y: b): () = foo() // expected-error{{tuple pattern has the wrong length for tuple type '()'}}
|
||||
// expected-error@-1 {{pattern of type '(x: _, y: _)' cannot match '()'}}
|
||||
}
|
||||
|
||||
// <rdar://problem/22322266> Swift let late initialization in top level control flow statements
|
||||
|
||||
@@ -16,5 +16,6 @@ extension S: P where N: P {
|
||||
// expected-error@-3 {{'A' is not a member type of type 'X'}}
|
||||
// expected-error@-4 {{'A' is not a member type of type 'X'}}
|
||||
return S<X.A>()
|
||||
// expected-error@-1 {{'A' is not a member type of type 'X'}}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user