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
[Sema][QoI] Call out missing conformances in protocol witness candidates. #19920
Conversation
…s in protocol inference to give better candidate failure notes.
@swift-ci Please smoke test. |
… thing to tell yourself.
@swift-ci Please smoke test. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cute.
@@ -1782,8 +1782,8 @@ NOTE(bad_associated_type_deduction,none, | |||
"unable to infer associated type %0 for protocol %1", | |||
(DeclName, DeclName)) | |||
NOTE(associated_type_deduction_witness_failed,none, | |||
"inferred type %1 (by matching requirement %0) is invalid: " | |||
"does not %select{inherit from|conform to}3 %2", | |||
"candidate would match and infer %0=%1 if %1 " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spaces around = might be more readable
lib/Sema/TypeCheckProtocol.cpp
Outdated
protocolType); | ||
|
||
type = type->mapTypeOutOfContext(); | ||
if (auto typeParamTy = type->getAs<GenericTypeParamType>()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if it’s a dependent member type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked for just above (that's one of the things I broke and needed the second commit for)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we generalize the DependentMemberType
to hasTypeParameter()
?
lib/Sema/TypeCheckProtocol.cpp
Outdated
assocType, protocolType); | ||
if (type->isEqual(conformance->getType())) { | ||
if (auto bgt = type->getAs<BoundGenericType>()) | ||
type = bgt->getDecl()->getDeclaredType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if it’s a non-generic type nested in a generic type? Is this restriction really necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, fix upcoming
test/stmt/foreach.swift
Outdated
@@ -17,7 +17,7 @@ func bad_containers_2(bc: BadContainer2) { | |||
} | |||
|
|||
struct BadContainer3 : Sequence { // expected-error{{type 'BadContainer3' does not conform to protocol 'Sequence'}} | |||
func makeIterator() { } // expected-note{{inferred type '()' (by matching requirement 'makeIterator()') is invalid: does not conform to 'IteratorProtocol'}} | |||
func makeIterator() { } // expected-note{{candidate would match and infer 'Iterator'='()' if '()' conformed to 'IteratorProtocol'}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, but () is not a nominal type, so it would never conform. Can you instead just say the return type is wrong without offering a suggestion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, more specific can't-conform-to-non-nominal and can't-inherit-from-non-class coming up.
For completeness you could do something similar for superclass constraints as well... but maybe those are rare enough that it doesn’t matter. |
@swift-ci Please smoke test. |
…rements so that we check superclass and same type requirements in the same way.
@slavapestov Added support for other types of requirements, so it gives similar messages for missing same-type and missing superclass. I was surprised that there was even an existing test that hit the new diagnosis for the same-type one - I thought that would be vanishingly rare. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These diagnostics are much-improved, thank you!
lib/Sema/TypeCheckProtocol.cpp
Outdated
protocolType); | ||
|
||
type = type->mapTypeOutOfContext(); | ||
if (auto typeParamTy = type->getAs<GenericTypeParamType>()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we generalize the DependentMemberType
to hasTypeParameter()
?
lib/Sema/TypeCheckProtocol.cpp
Outdated
// If the types would match but for some other missing conformance, find and | ||
// call that out. | ||
if (solution && conformance && solution->Fixes.size() == 1 && | ||
solution->Fixes.front()->getKind() == FixKind::AddConformance) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is more than one fix, perhaps we should diagnose the first fix? Or walk through the fixes and diagnose the first one we recognize? It won't describe the whole problem, but it's probably a better clue than falling back to "type mismatch."
@@ -1747,9 +1747,26 @@ bool AssociatedTypeInference::diagnoseNoSolutions( | |||
if (failed.Result.isError()) | |||
continue; | |||
|
|||
if (!failed.TypeWitness->is<NominalType>() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use getAnyNominal()
to check whether we have a nominal type here; is<NominalType>()
will only identify non-generic nominal types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... ugh, and I guess it's getAnyNominal() && !isExistentialType()
because of protocols. The type system's modeling of protocols is not true to the language w.r.t. protocols.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, missed the second part of this. Fix momentarily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, I'm confused. I think this is ok because the test is negative. An existential can't conform to a protocol and an existential is not a nominal. A protocol can conform, and a protocol is a nominal. Do I have part of this wrong?
failed.Result.getRequirement()); | ||
continue; | ||
} | ||
if (!failed.TypeWitness->is<ClassType>() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use getClassOrBoundGenericClass()
rather than is<ClassType>()
here.
continue; | ||
} | ||
if (!failed.TypeWitness->is<ClassType>() && | ||
!failed.Result.isConformanceRequirement()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, could !failed.Result.isConformanceRequirement()
be replaced by a positive check failed.Result.isSuperclassRequirement()
? If we get a third failure kind that's "not a conformance requirement" we'd produce the wrong diagnostic here.
@DougGregor Did those, thanks! |
@swift-ci Please smoke test. |
…fix checking into a separate function and try it with multiple fixes, if they exist.
Figured out my existential confusion, so fixed that, and implemented your suggestion of looking at all fixes, if there is more than one. |
@swift-ci Please smoke test. |
Continuing on to try to further improve inference QoI: Let CS::solveSingle() optionally allow fixes in the solution. Use this in protocol inference to look for fixes due to missing conformances and give better candidate failure notes. (Which were previously types-didn't-match notes that didn't really say anything about the actual problem.) Also changed the diagnosis text for the existing associated type inference failure due to missing requirement so that it is phrased similarly.
Thus, new candidate notes like: