Skip to content

[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

Merged
merged 1 commit into from
Jul 25, 2024

Conversation

gregomni
Copy link
Contributor

@gregomni gregomni commented Jul 2, 2024

Similar logic to the diagnoses already happening in visitUnresolvedSpecializeExpr in CSGen, but checking again after there are no more type variables resolves #74857.

@gregomni
Copy link
Contributor Author

gregomni commented Jul 2, 2024

@swift-ci Please smoke test.

Copy link
Collaborator

@AnthonyLatsis AnthonyLatsis left a 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
}

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni gregomni requested a review from AnthonyLatsis July 17, 2024 14:05
@gregomni
Copy link
Contributor Author

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 test/decl/ext/specialize.swift: rdar://111059036
Is there a diagnostic wanted here specifically for trying to specialize Self? (Now easy to add, if so.)

Copy link
Contributor

@xedin xedin left a 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.

@gregomni gregomni changed the title [Sema] Recheck UnresolvedSpecializeExpr after type variables are gone. [Sema] Add specialization constraints for func and variable types, then diagnose w/fixes. Jul 20, 2024
@gregomni
Copy link
Contributor Author

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.

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 Parse/generic_disambiguation.swift:29:

var a, b, c, d : Int
(a < b, c > (d))

Where if you create the constraint you also get extraneous errors about b and c not being known types. But for everything else it now just creates the specialization constraint and diagnoses via the fix for it.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni gregomni requested a review from xedin July 20, 2024 22:47
@xedin
Copy link
Contributor

xedin commented Jul 21, 2024

Thanks! I will take a look on monday!

@gregomni
Copy link
Contributor Author

Smoke test failed on Linux because a note was slightly different (different matching types on operator '<'). Replaced with substring of emitted note.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni gregomni force-pushed the generic-arg branch 2 times, most recently from b721de1 to ecbf057 Compare July 21, 2024 17:20
@gregomni
Copy link
Contributor Author

Also added a commit that improves parsing to avoid many cases of unary postfix '>'.

@swift-ci Please smoke test.

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;
Copy link
Contributor

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?

Copy link
Member

@rintaro rintaro Jul 23, 2024

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.

@gregomni
Copy link
Contributor Author

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.

@gregomni gregomni requested a review from xedin July 23, 2024 19:33
Copy link
Contributor

@xedin xedin left a 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);
Copy link
Contributor

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

Copy link
Contributor Author

@gregomni gregomni Jul 24, 2024

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

if (addSpecializationConstraint(overloadLocator, baseTy->getMetatypeInstanceType(),
expr->getUnresolvedParams())) {
// Diagnosis of failed type lookup - add a bit of context
auto &de = CS.getASTContext().Diags;
Copy link
Contributor

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.

Copy link
Contributor Author

@gregomni gregomni Jul 24, 2024

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)

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gotcha. Thanks for expanding.

@gregomni
Copy link
Contributor Author

Okay! Feedback commit, round 2. Unfortunately, I have some questions about what you're asking for, so this isn't likely to be final.

@gregomni gregomni requested a review from xedin July 24, 2024 04:58
@gregomni
Copy link
Contributor Author

Ok, I think maybe done. Squashed changes so far.

@rintaro's suggestion of how to do the Parse change works perfectly.
The constraints are now added unsolved and then activated.

@swift-ci Please smoke test.

@rintaro
Copy link
Member

rintaro commented Jul 24, 2024

@swift-ci Please smoke test

Copy link
Contributor

@xedin xedin left a 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));
Copy link
Contributor

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.

Copy link
Contributor Author

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.
@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Generic argument list incorrectly allowed on member
5 participants