Complete support is behind a flag, because it can result in a non-convergent
rewrite system if the opaque result type has a recursive conformance of its
own (eg, `some View` for SwiftUI's View protocol).
Without the flag, it's good enough for simple examples; you just can't have
a requirement that mentions a nested type of a type parameter equated to
the concrete type.
Fixes rdar://problem/88135291, https://bugs.swift.org/browse/SR-15983.
Instead of keeping track of the best one so far, and unifying subsequent rules
against it.
This allows us to record more identities, fixing a regression from an earlier
change where we were unable to eliminate an obviously-redundant rule.
- Rename StepLimit to MaxRuleCount, DepthLimit to MaxRuleLength
- Rename command line flags to -requirement-machine-max-rule-{count,length}=
- Check limits outside of PropertyMap::buildPropertyMap()
- Simplify the logic in RequirementMachine::computeCompletion()
If a type parameter is subject to both a conformance requirement
and a concrete type requirement, the concrete type might conform
conditionally.
In this case, introduce new requirements to satisfy the conditional
conformance.
Since this can add new hitherto-unseen protocols to the rewrite
system, restrict this feature to top-level generic signatures, and
not protocol requirement signatures. Allowing this to occur in
protocol requirement signatures would change the connectivity of
the protocol dependency graph (and hence the connected components)
during completion, which would be a major complication in the
design. The GSB already enforces this restriction.
I changed the existing conditional_requirement_inference.swift test
to run with -requirement-machine-inferred-signatures=verify. Since
one of the test cases there triggers an unrelated bug in the
Requirement Machine, I split it off into a new file named
conditional_requirement_inference_2.swift which still runs with
the GSB. Once the bug is fixed I'll merge the files again.
A superclass requirement 'T : C' implies a layout requirement
'T : AnyObject' (if the class is @objc) or 'T : _NativeObject'
(if the class is not @objc).
In the latter case, there might already be a 'T : AnyObject'
requirement, in which case the type parameter 'T' is subject
to two layout requirements:
T : AnyObject
T : _NativeObject
The second requirement implies the first however. To encode this
in the world of rewrite loops, we the notion of a 'relation'
between property symbols, and a 'Relation' rewrite step.
Here, the relation is that _NativeObject < AnyObject. Once this
relation is recorded, the Relation rewrite step will transform
T.[layout: _NativeObject].[layout: AnyObject]
into
T.[layout: _NativeObject]
and vice versa.
This rewrite step allows us to construct a rewrite loop which
makes the first rewrite rule redundant via the second.
For homotopy reduction to properly deal with new rules introduced
while bulding the property map, rewrite loops must be recorded
relating these to existing rules.
For homotopy reduction to properly deal with rewrite rules
introduced by property map construction, we need to keep
track of the original property rules when recording properties
in property bags.
For now, this doesn't even attempt to handle unification or
conflicts; we only record the first rule for each kind of
property on a fixed key.
This isn't actually used for anything yet, except a new
verify pass that runs after property map construction.
Now that the property map stores a reference to the RewriteSystem,
defining the buildPropertyMap() method on PropertyMap feels cleaner,
and allows more of the property map's internals to be private.
The property map stores the concrete type or superclass bound for all
terms whose suffix is equal to some key, so eg if you have
protocol P {
associatedtype T where T == U?
associatedtype U
}
Then you have this rewrite system
[P].T => [P:T]
[P].U => [P:U]
[P:T].[concrete: Optional<τ_0_0> with <[P:U]>] => [P:T]
Which creates this property map
[P:T] => { [concrete: Optional<τ_0_0> with <[P:U]>] }
Now if I start with a generic signature like
<T, U where U : P>
This produces the following rewrite system:
τ_0_1.[U] => τ_0_1
τ_0_1.T => τ_0_1.[P:T]
τ_0_1.U => τ_0_1.[P:U]
Consider the computation of the canonical type of τ_0_1.T. This term
reduces to τ_0_1.[P:T]. The suffix [P:T] has a concrete type symbol in
the property map, [concrete: Optional<τ_0_0> with <[P:U]>].
However it would not be correct to canonicalize τ_0_1.[P:T] to
Optional<τ_0_0>.subst { τ_0_0 => getTypeForTerm([P:T]) }; this
produces the type Optional<τ_0_0.T>, and not Optional<τ_0_1.T> as
expected.
The reason is that the substitution τ_0_0 => getTypeForTerm([P:T])
is "relative" to the protocol Self type of P, since domain([P:T]) = {P}.
Indeed, the right solution here is to note that τ_0_1.[P:T] has a suffix
equal to the key of the property map entry, [P:T]. If we strip off the
suffix, we get τ_0_1. If we then prepend τ_0_1 to the substitution term,
we get the term τ_0_1.[P:U], whose canonical type is τ_0_1.U.
Now, applying the substitution τ_0_0 => τ_0_1.U to Optional<τ_0_0>
produces the desired result, Optional<τ_0_1.U>.
Note that this is the same "prepend a prefix to each substitution of
a concrete type symbol" operation that is performed in checkForOverlap()
and PropertyBag::copyPropertiesFrom(), however we can simplify things
slightly by open-coding it instead of calling the utility method
prependPrefixToConcreteSubstitutions(), since the latter creates a
new symbol, which we don't actually need.