Skip to content

[ConstraintSystem] Allow fixing missing conformance failures for Void and uninhabited types. #27123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3971,11 +3971,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
}

// Let's not try to fix missing conformance for Void
// or Never because that doesn't really make sense.
if (type->isVoid() || type->isUninhabited())
return SolutionKind::Error;

if (path.back().is<LocatorPathElt::AnyRequirement>()) {
// If this is a requirement associated with `Self` which is bound
// to `Any`, let's consider this "too incorrect" to continue.
Expand Down
4 changes: 2 additions & 2 deletions test/Constraints/rdar44770297.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ protocol P {
associatedtype A
}

func foo<T: P>(_: () throws -> T) -> T.A? { // expected-note {{in call to function 'foo'}}
func foo<T: P>(_: () throws -> T) -> T.A? { // expected-note {{where 'T' = 'Never'}}
fatalError()
}

let _ = foo() {fatalError()} & nil // expected-error {{generic parameter 'T' could not be inferred}}
let _ = foo() {fatalError()} & nil // expected-error {{global function 'foo' requires that 'Never' conform to 'P'}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clear improvement!

However I can't help but wonder about 'Void'. The diagnostic would say "global function 'foo' requires that 'Void' conform to 'P'", which is a true statement, but 'Void' cannot ever conform to 'P'. Same if it's a function type or a metatype. As a follow-up you might want to add some test cases for non-nominal types and see if the diagnostic could be tailored a bit.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that there should be a tailored diagnostic. For example, this code:

protocol P {}

func genericFunc<T: P>(_ x: T) {}

genericFunc { return }

currently produces the error "Argument type '() -> ()' does not conform to expected type 'P'".

We already give a good error message for protocol types - "Protocol type 'P' cannot conform to 'P' because only concrete types can conform to protocols" - we could use a similar error message for non-nominal types.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do this in a separate PR

7 changes: 5 additions & 2 deletions test/type/opaque.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,11 @@ func recursion(x: Int) -> some P {
return recursion(x: x - 1)
}

// FIXME: We need to emit a better diagnostic than the failure to convert Never to opaque.
func noReturnStmts() -> some P { fatalError() } // expected-error{{cannot convert return expression of type 'Never' to return type 'some P'}} expected-error{{no return statements}}
func noReturnStmts() -> some P {} // expected-error {{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}}

func returnUninhabited() -> some P { // expected-note {{opaque return type declared here}}
fatalError() // expected-error{{return type of global function 'returnUninhabited()' requires that 'Never' conform to 'P'}}
}

func mismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> some P { // expected-error{{do not have matching underlying types}}
if x {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct Generic<T> {

@_functionBuilder
struct Builder {
static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1)
static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) // expected-note {{where 'C0' = 'Empty'}} expected-note {{where 'C1' = 'Test<Empty>'}}
-> Generic<(C0, C1)> where C0 : P, C1 : P {
return Generic((c0, c1))
}
Expand All @@ -24,11 +24,13 @@ struct Empty {
init() {}
}

struct Test<T> where T : P {
struct Test<T> where T : P { // expected-note {{where 'T' = 'Empty'}}
init(@Builder _: () -> T) {}
}

let x = G {
// expected-error@-1 {{static method 'buildBlock' requires that 'Empty' conform to 'P'}}
// expected-error@-2 {{static method 'buildBlock' requires that 'Test<Empty>' conform to 'P'}}
Empty()
Test { <#code#> } // expected-error {{editor placeholder in source file}}
Test { Empty() } // expected-error {{generic struct 'Test' requires that 'Empty' conform to 'P'}}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

protocol P {}

func fn<T, U: P>(_ arg1: T, arg2: (T) -> U) {}
func fn<T, U: P>(_ arg1: T, arg2: (T) -> U) {} // expected-note {{where 'U' = '()'}}

func test(str: String) {
fn(str) { arg in
fn(str) { arg in // expected-error {{global function 'fn(_:arg2:)' requires that '()' conform to 'P'}}
<#FOO#> // expected-error {{editor placeholder in source file}}
}
}