-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Preliminary Sema and AST support for subclass existentials (SE-0156) #8650
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
Preliminary Sema and AST support for subclass existentials (SE-0156) #8650
Conversation
6996814
to
3e8bb89
Compare
|
||
/// Whether the existential requires a class, either via an explicit | ||
/// '& AnyObject' member or because of a superclass or protocol constraint. | ||
bool requiresClass : 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.
Should this more generally have a LayoutConstraintKind
?
|
||
auto members = type->getProtocols(); | ||
if (!members.empty() && | ||
isa<ClassDecl>(members[0]->getAnyNominal())) { |
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.
getClassOrBoundGenericClass()
would be slightly more direct.
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 doesn't support UnboundGenericType, which is possible here.
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.
Ah, okay!
// Only protocols can appear in the inheritance clause | ||
// of a protocol -- anything else should get diagnosed | ||
// elsewhere. | ||
if (auto *protoTy = type->getAs<ProtocolType>()) { |
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, really? I thought we could have typealiases of protocol compositions, as well as superclass types, in this list?
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'll add more tests and see what we support exactly.
lib/AST/ASTContext.cpp
Outdated
for (Type t : Protocols) { | ||
if (!t->isCanonical()) | ||
isCanonical = false; | ||
properties |= t->getRecursiveProperties(); | ||
} | ||
|
||
// Create a new protocol composition type. | ||
ProtocolCompositionType *New | ||
= new (C, AllocationArena::Permanent) |
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.
Wrong arena! You'll need to compute the arena based on the recursive properties.
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.
Good point, thanks!
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 just wanted to give @jrose-apple another fun memory smasher bug to track down :)
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.
Just think of how much time we saved with this one little bit of code review.
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.
*shudder*
"explicit AnyObject not yet supported"); | ||
|
||
if (layout.superclass) { | ||
if (addSuperclassRequirementDirect(subjectPA, layout.superclass, |
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.
Thanks for fixing me FIXME.
…substitutions This allows always using the constructor that takes substitutions, passing in an empty list if there are none.
This allows calling simplifyType() unconditionally even if we know we don't have any type variables in the type.
Don't pass in the opened type; instead have the caller call simplifyType() if needed. Also, make computeSubstitutions() bail out if there's no generic signature, which allowed unifying several generic vs non-generic code paths. Hopefully there is enough short circuiting in there now that we're not doing any extra work in the non-generic case.
Now that we no longer map dependent member types to fresh type variables, the keys in the replacement map can just be GenericTypeParamTypes.
…ed 'Self' A protocol extension can add additional generic constraints on 'Self' or associated types thereof. In particular, 'Self' itself can have a superclass constraint placed on it. There were a couple of problems with this corner case: - Type aliases defined in protocols that 'Self' conforms to _as a concrete type_ to were not handled properly, triggering an assertion. For example, protocol P { typealias T = ... } class C : P {} protocol Q {} extension Q where Self : C { ... T ... } The conformance o P comes from the 'Self : C' constraint. - If the type was found in a superclass of 'Self', we used the wrong base type for the substitution. For example, protocol P {} class C<T> { typealias A = T } class D : C<Int> {} extension P where Self : D { ... A ... } The substituted type of 'A' should be computed with a self type of C<Int> here. Also, take another stab at cleaning up the mess that is resolveTypeInContext() and related bits of code.
…trained archetype This hits the FIXME case in CompleteGenericTypeResolver's resolveDependentMemberType() method I thought was dead, but it's actually not.
This consolidates calculations which need to look at every protocol in an existential type. Soon we will also have to deal with superclass constrained existentials, so start updating call sites that look at all protocols to use the new ExistentialLayout and correctly handle a class constraint as well. Also, eventually I will kill off the AnyObject protocol and model it as a protocol composition with no protocols or superclass, but the requiresClass() flag set. This is not quite modeled this way yet and AnyObject still exists, but the new abstraction is a step in the right direction.
Don't assume all members are existential types; we could have a class (or a class-constrained existential) also, and this needs to be handled. When building a canonical protocol composition, the subclass requirement always comes first in the list of types. This is an important invariant allowing ExistentialLayout to be calculated quickly. Also, calculate the recursive properties of the composition type from the recursive properties of its members, since they're no longer trivial if the composition contains a class member with generic parameters.
…stentials Whether a protocol composition requires a class is no longer solely a property of what protocols it contains; now, a protocol composition consisting of a non-class constrained protocol and a superclass should return true from requiresClass(). This is done by looking at the ExistentialLayout, instead of walking the members directly.
The "superclass" of a subclass existential is the superclass constraint. This makes a bunch of random things work, but is somewhat unprincipled. It fits with how we already handle class-constrained archetypes and DynamicSelfType, though.
This function takes a member of a class and a base type, and returns the correct 'self' type to substitute into the member's type. When accessing a member of a subclass existential, if the member was found via the superclass constraint, we have to erase the existential down to the class type to calculate the member's substituted type. We already had special logic to handle class-constrained archetypes in the callers of getSuperclassForDecl(); move this check into the method, and add a similar check for subclass existentials, which now support the getSuperclass() method.
If the member came from a class, we're not going to substitute the 'Self' type. Instead, we open the existential, upcast the archetype up to the class type and access the member as if it were a class member in the usual way.
3e8bb89
to
143c29e
Compare
If the -enable-experimental-subclass-existentials staging flag is on, resolveType() now allows protocol compositions to contain class types. It also diagnoses if a composition has more than one superclass requirement. Also, change diagnostics that talked about 'protocol composition' to 'protocol-constrained type'. Since such types can now contain a superclass constraint, it's not correct to call them protocol composition. "Protocol-constrained type" isn't quite accurate either because 'Any' has no protocols, and 'AnyObject' will have no protocols but a general class constraint; but those are edge cases which won't come up in these diagnostics.
143c29e
to
de323b5
Compare
@swift-ci Please smoke test |
Work in progress.
TODO:
C & P
should be the same type asC
. While we can convert back and forth, they currently appear as distinct types for purposes of overloading, etc.C & D
should be equivalent toD
; for now, it is an error.C & C
is equivalent to C, though.