-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Sema] Add specialization constraints for func and variable types, then diagnose w/fixes. #74909
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
Conversation
@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.
Looks good, but I think we should be diagnosing more cases here, perhaps anything expect macro specializations and a DotSyntaxBaseIgnoredExpr
subsexpression, which is this sort of case:
extension Bool {
typealias A<T> = Bool
}
func foo(_ meta: Bool.Type) {
let _ = meta.A<Bool>.self
}
@swift-ci Please smoke test. |
I think I've resolved the issues and solidified this. Now delays checking of all apparent function types and diagnoses them all afterwards in CSApply (or if we would otherwise get an unbound generic parameter error -- there). Also, could you give me the gist of the request for this radar in |
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.
A more reliable way to implement this would be to introduce the specialization constraint during CSGen regardless of what the base type is and use it to introduce fixes when types are sufficiently resolved.
Diagnosing something in CSApply is in general too late because it means that the solver produced an invalid solution which shouldn't happen for things that could be checked during solving.
Done! I left the check in CSGen for known non-function types because diagnoses pointing at the type-parameter bracket is better for confusing situations like
Where if you create the constraint you also get extraneous errors about |
@swift-ci Please smoke test. |
Thanks! I will take a look on monday! |
Smoke test failed on Linux because a note was slightly different (different matching types on operator '<'). Replaced with substring of emitted note. |
@swift-ci Please smoke test. |
@swift-ci Please smoke test. |
b721de1
to
ecbf057
Compare
Also added a commit that improves parsing to avoid many cases of unary postfix '>'. @swift-ci Please smoke test. |
lib/Parse/ParseType.cpp
Outdated
return false; | ||
// If this is the end of the expr (wouldn't match parseExprSequenceElement), | ||
// prefer generic type list over an illegal unary postfix '>' operator. | ||
return tok.isKeyword() && tok.getKind() != tok::kw_try; |
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.
@rintaro could you take a look at this please?
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.
tok.isKeyword()
is usually not suitable for detecting "the token is not related to the current production" because many decls starts with a non-keyword. e.g.
let a = Foo<Bar>
open class Cls { ... }
P.isStartOfSwiftDecl() || P.isStartOfStmt()
might be better.
Did all the Sema review feedback. Will look at Parse when I get a chance -- or would it be easier if I did a separate PR for that part with a different reviewer? @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.
I think we are super close here, left a few more comments inline, also we are focusing on methods/functions but what about subscripts? It would be good to add a few test-cases for them as well.
if (result->hasError()) { | ||
auto &ctxt = CS.getASTContext(); | ||
auto *repr = new (ctxt) PlaceholderTypeRepr(specializationArg->getLoc()); | ||
result = PlaceholderType::get(ctxt, repr); |
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.
Instead of create a placeholder repr placeholder type should use original specializationArg
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.
The PlaceholderType Originator
argument requires specifically a PlaceholderTypeRepr
, although the uses don't seem to require anything specific to that subclass, except that LocatorPathElt::PlaceholderType
also requires it. So I could change both of those types to accept any TypeRepr
. Is that what you mean?
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.
It looks like replaceInferableTypesWithTypeVars
does this same pattern of creating a PlaceholderTypeRepr
, so I think this should be left as is.
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.
That method is actually going the other way - it detects if there are placeholders in a Type
and "opens" them. I think we should expand from PlaceholderTypeRepr to TypeRepr
in PlaceholderType
and locator element. But we can do that in a separate PR.
lib/Sema/CSGen.cpp
Outdated
if (addSpecializationConstraint(overloadLocator, baseTy->getMetatypeInstanceType(), | ||
expr->getUnresolvedParams())) { | ||
// Diagnosis of failed type lookup - add a bit of context | ||
auto &de = CS.getASTContext().Diags; |
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.
I still think we should delay simplification here to let "normal" solving fail and re-attempt during diagnostic mode.
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.
Sorry, I think you'll have to be more explicit, I'm not sure I understand.
CS.addConstraint()
happens inside of addSpecializationConstraint()
. If this is changed to be CS.addUnsolvedConstraint()
, then the solve for many of these cases will type check while leaving the specialization constraint in the Inactive list the whole time (because they really are extraneous), and then fail due to remaining inactive constraints (CSStep.cpp:482).
The if statement here was just for whether or not to add a note, the constraint is added no matter what. But I'll clean that part up. (So the note is emitted inside addSpecializationConstraint
)
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.
To be precise my suggestion was to remove the result type for addSpecializationConstraint
, inside of that method: create a new ExplicitGenericArguments
constraint, use addUnsolvedConstraint
+ activateConstraint
(usually activateConstraint
is not necessary but as far as I understand the main problem here that sometimes there are no type variables here for solver to bind and subsequently re-evaluate the constraint).
One trick to avoid explicit activation would be to allocate a new type variable and use an unsolved Equal
constraint to connect it to the boundType
, type variable itself should be used in place of boundType
in an unsolved ExplicitGenericArguments
constraint. This way the solver would pick the type variable and bind it during solving which activates all of the constraints that reference it.
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.
Gotcha. Thanks for expanding.
Okay! Feedback commit, round 2. Unfortunately, I have some questions about what you're asking for, so this isn't likely to be final. |
@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.
Thank you very much for all the work here!
@@ -9338,7 +9338,12 @@ bool InvalidMemberReferenceWithinInitAccessor::diagnoseAsError() { | |||
} | |||
|
|||
bool ConcreteTypeSpecialization::diagnoseAsError() { | |||
emitDiagnostic(diag::not_a_generic_type, ConcreteType); | |||
emitDiagnostic(diag::not_a_generic_type, resolveType(ConcreteType)); |
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.
Nit: I'd prefer the resolveType happened during construction of the diagnostic, it's usually safer that way.
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.
Done. That was an accidental leftover from the changes along the way.
Always add constraints, find fixes during simplify. New separate fix for allow generic function specialization. Improve parse heuristic for isGenericTypeDisambiguatingToken.
@swift-ci Please smoke test. |
Similar logic to the diagnoses already happening in visitUnresolvedSpecializeExpr in CSGen, but checking again after there are no more type variables resolves #74857.