Skip to content

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

Merged
merged 20 commits into from
Apr 11, 2017

Conversation

slavapestov
Copy link
Contributor

@slavapestov slavapestov commented Apr 9, 2017

Work in progress.

TODO:

  • If a class C conforms to P, then C & P should be the same type as C. While we can convert back and forth, they currently appear as distinct types for purposes of overloading, etc.
  • If a class C is a subclass of D, the proposal says C & D should be equivalent to D; for now, it is an error. C & C is equivalent to C, though.
  • Type alias lookup on a subclass existential is not quite right if the typealias is a member of a protocol that the superclass conforms to concretely.
  • Protocol inheritance clauses cannot contain superclasses yet.
  • AnyObject is not yet lowered away completely.
  • Mangling, SILGen, IRGen, serialization, runtime support.
  • Importing Objective-C subclass existentials.

@slavapestov slavapestov force-pushed the subclass-existentials branch 3 times, most recently from 6996814 to 3e8bb89 Compare April 10, 2017 08:29

/// Whether the existential requires a class, either via an explicit
/// '& AnyObject' member or because of a superclass or protocol constraint.
bool requiresClass : 1;
Copy link
Member

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())) {
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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>()) {
Copy link
Member

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?

Copy link
Contributor Author

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.

for (Type t : Protocols) {
if (!t->isCanonical())
isCanonical = false;
properties |= t->getRecursiveProperties();
}

// Create a new protocol composition type.
ProtocolCompositionType *New
= new (C, AllocationArena::Permanent)
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, thanks!

Copy link
Contributor Author

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 :)

Copy link
Member

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.

Copy link
Contributor

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,
Copy link
Member

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.
@slavapestov slavapestov force-pushed the subclass-existentials branch from 3e8bb89 to 143c29e Compare April 11, 2017 00:06
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.
@slavapestov slavapestov force-pushed the subclass-existentials branch from 143c29e to de323b5 Compare April 11, 2017 00:11
@slavapestov
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.

3 participants