Instead of walking over PotentialArchetypes representatives directly
and using a separate list to record same-type constraints, just use
enumerateRequirements() and check the RequirementSource to drop
redundant requirements.
This means getGenericSignature() and getCanonicalManglingSignature()
can share the same logic for collecting requirements; the only
differences are the following:
- both drop requirements from Redundant sources, but mangling
signatures also drop requirements from Protocol sources
- mangling signatures also canonicalize the types appearing in the
final requirement
Move the sorting algorithm from construction of the canonical mangling
signature to requirement enumeration.
This also changes how same-type constraints pick representatives, using
the more canonical total order.
We used RequirementSources to identify redundant requirements
when building the canonical mangling signature. There were a
few problems with how this worked:
- We used the Inferred requirement source for both requirements
that were inferred from function parameter and result types
(for example, T : Hashable in `func foo<T, U>(dict: [T : U])`
and some completely unrelated things, like same-type
constraints imposed on nested types by existing same-type
constraints on the outer types.
- We used the Protocol requirement source for associated type
requirements on protocols as well as conformance requirements
on inherited protocols.
- Introducing a new Redundant requirement did not mark existing
Explicit requirements as Redundant.
None of these were an issue because of how canonical mangling
signature construction works:
- We already started with a canonical signature, so there were
no Inferred requirements of the first kind (which we *do* want
to keep here)
- We dropped both Protocol and Redundant requirements, so it
didn't matter that the distinction here was not sufficiently
fine-grained
- We never introduced Explicit requirements that would be later
superceded by a Redundant requirement, because we always
started with an existing canonical signature where such Explicit
requirements were already dropped.
However, I want to use the same algorithm for building the
original signature as the canonical mangling signature, so this
patch introduces these changes:
- The Inferred source is now only used for inferred requirements of
the first kind; the second kind are now Redundant
- The Protocol source is now only used for associated type
requirements; inherited protocol conformances are now Redundant
- updateRequirementSource() now does the right thing with
introduced Redundant requirements
For now, this doesn't change much, but an upcoming patch
refactors ArchetypeBuilder::getGenericSignature() to also
use enumerateRequirements(), except dropping Redundant
requirements only, and not Protocol.
These will be more useful once substitutions in protocol conformances
are moved to use interface types.
At first, these are only going to be used by the SIL optimizer.
A GenericEnvironment stores the mapping between GenericTypeParamTypes
and context archetypes (or eventually, concrete types, once we allow
extensions to constrain a generic parameter to a concrete type).
The goals here are two-fold:
- Eliminate the GenericTypeParamDecl::getArchetype() method, and
always use mapTypeIntoContext() instead
- Replace SILFunction::ContextGenericParams with a GenericEnvironment
This patch adds the new data type as well as serializer and AST
verifier support. but nothing else uses it yet.
Note that GenericSignature::get() now asserts if there are no
generic parameters, instead of returning null. This requires a
few tweaks here and there.
This is the opposite of GenericSignature::getSubstitutionMap(),
transforming an interface type substitution mapping into
a Substitution array.
Previously, Substitution arrays were built with hand-rolled
logic, usually relying on a GenericParamList's AllArchetypes
list. We want to stop using the AllArchetypes list, instead
using the requirements array from a GenericSignature.
Nothing calls this method yet; existing code will be refactored
to call it in the next few patches.
This is obviously the right thing to do in terms of ensuring
that two different expressions of the same signature always result
in the same type. It also has the pleasant side-effect of causing
the canonical function type to never be expressed in terms of type
parameters which have been equated with concrete types, which means
that various consumers that work primarily with canonical types
(such as SILGen and IRGen) no longer have to worry about such types,
at least when decomposing a generic function signature.
from the witness tables for their associations rather than passing
them separately.
This drastically reduces the number of physical arguments required
to invoke a generic function with a complex protocol hierarchy. It's
also an important step towards allowing recursive protocol
constraints. However, it may cause some performance problems in
generic code that we'll have to figure out ways to remediate.
There are still a few places in IRGen that rely on recursive eager
expansion of associated types and protocol witnesses. For example,
passing generic arguments requires us to map from a dependent type
back to an index into the all-dependent-types list in order to
find the right Substitution; that's something we'll need to fix
more generally. Specific to IRGen, there are still a few abstractions
like NecessaryBindings that use recursive expansion and are therefore
probably extremely expensive under this patch; I intend to fix those
up in follow-ups to the greatest extent possible.
There are also still a few things that could be made lazier about
type fulfillment; for example, we eagerly project the dynamic type
metadata of class parameters rather than waiting for the first place
we actually need to do so. We should be able to be lazier about
that, at least when the parameter is @guaranteed.
Technical notes follow. Most of the basic infrastructure I set up
for this over the last few months stood up, although there were
some unanticipated complexities:
The first is that the all-dependent-types list still does not
reliably contain all the dependent types in the minimized signature,
even with my last patch, because the primary type parameters aren't
necessarily representatives. It is, unfortunately, important to
give the witness marker to the primary type parameter because
otherwise substitution won't be able to replace that parameter at all.
There are better representations for all of that, but it's not
something I wanted to condition this patch on; therefore, we have to
do a significantly more expensive check in order to figure out a
dependent type's index in the all-dependent-types list.
The second is that the ability to add requirements to associated
types in protocol refinements means that we have to find the *right*
associatedtype declaration in order to find the associated witness
table. There seems to be relatively poor AST support for this
operation; maybe I just missed it.
The third complexity (so far) is that the association between an
archetype and its parent isn't particularly more important than
any other association it has. We need to be able to recover
witness tables linked with *all* of the associations that lead
to an archetype. This is, again, not particularly well-supported
by the AST, and we may run into problems here when we eliminate
recursive associated type expansion in signatures.
Finally, it's a known fault that this potentially leaves debug
info in a bit of a mess, since we won't have any informaton for
a type parameter unless we actually needed it somewhere.
Parse 'var [behavior] x: T', and when we see it, try to instantiate the property's
implementation in terms of the given behavior. To start out, behaviors are modeled
as protocols. If the protocol follows this pattern:
```
protocol behavior {
associatedtype Value
}
extension behavior {
var value: Value { ... }
}
```
then the property is instantiated by forming a conformance to `behavior` where
`Self` is bound to the enclosing type and `Value` is bound to the property's
declared type, and invoking the accessors of the `value` implementation:
```
struct Foo {
var [behavior] foo: Int
}
/* behaves like */
extension Foo: private behavior {
@implements(behavior.Value)
private typealias `[behavior].Value` = Int
var foo: Int {
get { return value }
set { value = newValue }
}
}
```
If the protocol requires a `storage` member, and provides an `initStorage` method
to provide an initial value to the storage:
```
protocol storageBehavior {
associatedtype Value
var storage: Something<Value> { ... }
}
extension storageBehavior {
var value: Value { ... }
static func initStorage() -> Something<Value> { ... }
}
```
then a stored property of the appropriate type is instantiated to witness the
requirement, using `initStorage` to initialize:
```
struct Foo {
var [storageBehavior] foo: Int
}
/* behaves like */
extension Foo: private storageBehavior {
@implements(storageBehavior.Value)
private typealias `[storageBehavior].Value` = Int
@implements(storageBehavior.storage)
private var `[storageBehavior].storage`: Something<Int> = initStorage()
var foo: Int {
get { return value }
set { value = newValue }
}
}
```
In either case, the `value` and `storage` properties should support any combination
of get-only/settable and mutating/nonmutating modifiers. The instantiated property
follows the settability and mutating-ness of the `value` implementation. The
protocol can also impose requirements on the `Self` and `Value` types.
Bells and whistles such as initializer expressions, accessors,
out-of-line initialization, etc. are not implemented. Additionally, behaviors
that instantiate storage are currently only supported on instance properties.
This also hasn't been tested past sema yet; SIL and IRGen will likely expose
additional issues.
When a dependent type is mapped into context, the result will either be
an archetype or a concrete type. The latter occurs if a same-type
constraint exists between the dependent type and the concrete type.
The logic to decide if a type should be passed directly or indirectly
was not handling this case if an interface type was passed down -- we
would just check if there was a class constraint present.
This resulted in mismatching conventions between an interface type and
its corresponding contextual type, which would trigger assertions.
Note that same-type constraints between generic parameters and concrete
types are still not supported for other reasons; the subject of the
constraint must still be an associated type of a generic parameter.
Fixes <rdar://problem/24687460>.
As part of this, use a different enum for parsed generic requirements.
NFC except that I noticed that ASTWalker wasn't visiting the second
type in a conformance constraint; fixing this seems to have no effect
beyond producing better IDE annotations.
This eliminates some minor overheads, but mostly it eliminates
a lot of conceptual complexity due to the overhead basically
appearing outside of its context.
Extend GenericSignature to be able to answer queries about the
requirements placed on dependent types, e.g, are the class-bound, to
what protocols must they conform, etc. Implement this using a
lazily-created ArchetypeBuilder on the canonical generic signature.
NFC and as-yet-untested; this is staging for reducing our dependence
on the "all archetypes" list.
Swift SVN r32340
GenericSignature's factory method determining whether the signature
was canonical based solely on whether the types in the parameters and
requirments were canonical. While that is currently true (for legacy
reasons), it is wrong: canonicalization also needs to canonicalize
requirements, including same-type requirements, as is currently done
in the canonical signature "for mangling". Move the "this is
canonical" dependency to the point where the canonical signature is
actually computed, so we can change the definition of canonical
signatures later.
While we're here, don't eagerly compute the canonical generic
signature in GenericSignature::getASTContext().
Swift SVN r32309