-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Retain type sugar for extension declarations that name generic typealiases #15450
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
Retain type sugar for extension declarations that name generic typealiases #15450
Conversation
…iases. When extending a type via a generic typealias, where the type parameters of the underlying nominal type line up precisely with those of the generic typealias and its specialization of the underlying nominal type (a so-called "pass-through" typealias in the new code), maintain type sugar in the extension declaration. This new type sugar enables inference of type requirements from the generic typealias, which is both useful by itself (it lets the type requirements on generic typealiases be meaningful for extensions like they are elsewhere), and also addresses a source-compatability regression where an extension of `CountableRange` will now infer the requirement `Bound: Comparable`. Fixes SR-6907 / rdar://problem/29066394.
@swift-ci please smoke test |
@swift-ci please test source compatibility |
|
||
struct RequiresComparable<T: Comparable> { } | ||
|
||
extension CountableRange { // expected-warning{{'CountableRange' is deprecated: renamed to 'Range'}} |
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.
Can you test 'extension DictionaryIndex' too?
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.
Sure!
This function was unable to "look through" an unbound generic typealias to find the underlying nominal type that was getting extended, which can occur if we end up performing a name lookup into this context before the extension itself has been fully-checked. Also, test that extending DictionaryIndex works.
@swift-ci please smoke test |
@swift-ci please test source compatibility |
The optimization that avoided forming new generic signatures for extensions of nominal types was kicking in too eagerly, preventing us from inferring requirements from the typealias.
@swift-ci please smoke test |
@swift-ci please test source compatibility |
1 similar comment
@swift-ci please test source compatibility |
@swift-ci please smoke test |
Linux failure is weird:
|
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.
LGTM, but I don't have this stuff paged into my head anymore.
auto type = ext->getExtendedType(); | ||
if (!type) return nullptr; | ||
|
||
do { |
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.
Trivial style point, but a while(true) is arguably better than do/while(true) because you can know that it is an infinite loop reading the code top to bottom.
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.
Yeah, sure.
// Check that the type parameters are the same the whole way through. | ||
auto nominalGenericParams = nominalSig->getGenericParams(); | ||
auto typealiasGenericParams = typealiasSig->getGenericParams(); | ||
if (nominalGenericParams.size() != typealiasGenericParams.size()) |
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.
Checking the sizes match is redundant with the subsequent std::equal call.
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.
Unfortunately, it isn't, because std::equal
is terrible API that only takes the "first" iterator to the second sequence, assuming that one has already performed the size check ahead of time!
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.
Oh man. 🙄🙄
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, there's the variant that takes the second end-iterator too…
// a passthrough typealias. | ||
auto innermostGenericParams = typealiasSig->getInnermostGenericParams(); | ||
auto boundArgs = boundGenericType->getGenericArgs(); | ||
if (boundArgs.size() != innermostGenericParams.size()) |
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.
likewise.
auto nominal = dyn_cast<NominalTypeDecl>(genericDecl); | ||
auto typealias = dyn_cast<TypeAliasDecl>(genericDecl); | ||
if (!nominal) { | ||
Type underlyingType = typealias->getUnderlyingTypeLoc().getType(); |
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.
Are you sure that genericDecl is either a typealias or a nominaltype decl? If it is possible for it to be neither of those, then this code will crash with a null dereference.
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 am, because the only forms of type declaration we have in the language are "nominal" and "alias". I suspect that distinction will last "forever", because any new kind of type we invent will either have a name and a runtime presence (nominal) or be some form of alias.
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.
Ok, sounds good. please add an assertion :)
Okay, this one is actually good to go. The GRDB source-compatibility failure can't be addressed in a meaningful way. @lattner , I have another commit coming to fix the |
I feel sorry (GRDB author here). Is there anything I can do to help? |
That's a relief, thank you Doug! Because the work that had to be done to make it pass was everything but trivial :-) |
You gave me an excuse to refactor something that I've felt is wrong/broken for ~2 years, so thanks! |
When extending a type via a generic typealias, where the type parameters of
the underlying nominal type line up precisely with those of the
generic typealias and its specialization of the underlying nominal
type (a so-called "pass-through" typealias in the new code), maintain
type sugar in the extension declaration.
This new type sugar enables inference of type requirements from the
generic typealias, which is both useful by itself (it lets the type
requirements on generic typealiases be meaningful for extensions like
they are elsewhere), and also addresses a source-compatability
regression where an extension of
CountableRange
will now infer therequirement
Bound: Comparable
.Fixes SR-6907 / rdar://problem/29066394.