When inserting a new value, assert that there's no existing
value at that key, and to avoid the assert firing when
opening up UnboundGenericTypes, put those replacements in
their own map. We don't need them later.
Protocol methods returning optionals, metatypes and functions
returning 'Self' are now handled correctly in Sema when
accessed with a base of existential type, eg:
protocol Clonable {
func maybeClone() -> Self?
func cloneMetatype() -> Self.Type
func getClonerFunc() -> () -> Self
}
let c: Clonable = ...
let _: Clonable = c.maybeClone()
let _: Clonable.Type = c.cloneMetatype()
Previously, we were unable to model this properly, for
several reasons:
- When opening the function type, we replaced the return
value in openedFullType _unconditionally_ with the
existential type. This was broken if the return type
was not the 'Self' parameter, but rather an optional,
metatype or function type involving the 'Self' parameter.
- It was also broken because we lost information; if the
return type originally contained an existential, we had
no way to draw the distinction. The 'hasArchetypeSelf()'
method was a hint that the original type contained a
type parameter, but it was inaccurate in some subtle cases.
- Since we performed this replacement on both the
'openedFullType' and 'openedType', CSApply had to "undo"
it to replace the existential type with the opened
existential archetype. This is because while the formal
type of the access returns the existential type, in
reality it returns the opened type, and CSApply has to
insert the correct ErasureExpr.
This was also done unconditionally, but even if it were
done more carefully, it would do the wrong thing because
of the 'loss of information' above.
- There was something fishy going on when we were dealing
with a constructor that was declared on the Optional type
itself, as seen in the fixed type_checker_crasher.
- TypeBase::eraseOpenedExistential() would transform a
MetatypeType of an opened existential into a MetatypeType
of an existential, rather than an ExistentialMetatypeType
of the existential type.
The new logic has the following improvements:
- When opening a function type, we replace the 'Self' type
parameter with the existential type in 'openedType', but
*not* 'openedFullType'. This simplifies CSApply, since it
doesn't have to "undo" this transformation when figuring
out what coercions to perform.
- We now only use replaceCovariantResultType() when working
with Self-returning methods on *classes*, which have more
severe restrictions than protocols. For protocols, we walk
the type using Type::transform() and handle all the cases
properly.
- Not really related to the above, but there was a whole
pile of dead code here concerning associated type references.
Note that SILGen still needs support for function conversions
involving an existential erasure; in the above Clonable example,
Sema now models this properly but SILGen still crashes:
let _: () -> Clonable = c.getClonerFunc()
Fixes <rdar://problem/28048391>, progress on <rdar://problem/21391055>.
This patch contains several intertwined changes:
- Remove some unnecessary complexity and duplication.
- Adds a new TypeChecker::lookupUnqualifiedType() which bypasses most of
the logic in TypeChecker::lookupUnqualified(), such as the
LookupResultBuilder. Use this when resolving unqualified references
to types.
- Fixes for generic typealiases to better preserve the type parameters of
the parent type, and clean up the logic for applying the inner generic
arguments. Some uses of generic typealiases that used to crash now work,
and those tests have been uncommented.
- Avoid an unnecessary desugaring of TypeAliasDecls which map directly
to GenericTypeParamTypes. Once again this perturbs the source-stability
test.
- When looking up a nested type of a base class with a derived class base,
always use the base class as the parent of the nested type. This fixes
a recent regression where in some cases we were using the wrong parent.
Fixes <rdar://problem/29782186>.
If T0 must be materializable and it's bound to T1, when matching T0 to
possibly optinal T1, look through optinality when setting materializability of the binding.
Currently isAnyHashableType method validates only structs but
it doesn't account for situation when type is represented via type
variable with type already fixed to AnyHashable, that results in
infinite loop in the solver because restriction [hashable-to-
anyhashable] is going to add new type variable recursively.
withoutActuallyEscaping has a signature like `<T..., U, V, W> (@nonescaping (T...) throws<U> -> V, (@escaping (T...) throws<U> -> V) -> W) -> W, but our type system for functions unfortunately isn't quite that expressive yet, so we need to special-case it. Set up the necessary type system when resolving an overload set to reference withoutActuallyEscaping, and if a type check succeeds, build a MakeTemporarilyEscapableExpr to represent it in the type-checked AST.
`type(of:)` has behavior whose type isn't directly representable in Swift's type system, since it produces both concrete and existential metatypes. In Swift 3 we put in a parser hack to turn `type(of: <expr>)` into a DynamicTypeExpr, but this effectively made `type(of:)` a reserved name. It's a bit more principled to put `Swift.type(of:)` on the same level as other declarations, even with its special-case type system behavior, and we can do this by special-casing the type system we produce during overload resolution if `Swift.type(of:)` shows up in an overload set. This also lays groundwork for handling other declarations we want to ostensibly behave like normal declarations but with otherwise inexpressible types, viz. `withoutActuallyEscaping` from SE-0110.
Follow the pattern set by isDictionaryType() of performing the query
and extracting the underlying key/element types directly. We often
need both regardless. NFC
- The DeclContext versions of these methods have equivalents
on the DeclContext class; use them instead.
- The GenericEnvironment versions of these methods are now
static methods on the GenericEnvironment class. Note that
these are not made redundant by the instance methods on
GenericEnvironment, since the static methods can also be
called with a null GenericEnvironment, in which case they
just assert that the type is fully concrete.
- Remove some unnecessary #includes of ArchetypeBuilder.h
and GenericEnvironment.h. Now changes to these files
result in a lot less recompilation.
Changes:
* Terminate all namespaces with the correct closing comment.
* Make sure argument names in comments match the corresponding parameter name.
* Remove redundant get() calls on smart pointers.
* Prefer using "override" or "final" instead of "virtual". Remove "virtual" where appropriate.
- TypeAliasDecl::getAliasType() is gone. Now, getDeclaredInterfaceType()
always returns the NameAliasType.
- NameAliasTypes now always desugar to the underlying type as an
interface type.
- The NameAliasType of a generic type alias no longer desugars to an
UnboundGenericType; call TypeAliasDecl::getUnboundGenericType() if you
want that.
- The "lazy mapTypeOutOfContext()" hack for deserialized TypeAliasDecls
is gone.
- The process of constructing a synthesized TypeAliasDecl is much simpler
now; instead of calling computeType(), setInterfaceType() and then
setting the recursive properties in the right order, just call
setUnderlyingType(), passing it either an interface type or a
contextual type.
In particular, many places weren't setting the recursive properties,
such as the ClangImporter and deserialization. This meant that queries
such as hasArchetype() or hasTypeParameter() would return incorrect
results on NameAliasTypes, which caused various subtle problems.
- Finally, add some more tests for generic typealiases, most of which
fail because they're still pretty broken.
Once we've bound a type variable, we find those inactive constraints
that mention the type variable and make them active, so they'll be
simplified again. However, we weren't finding *all* constraints that
could be affected---in particular, we weren't searching everything
related to the type variables in the equivalence class, which meant
that some constraints would not get visited... and we would to
type-check simply because we didn't look at a constraint again when we
should have.
Fixes rdar://problem/29633747.
There is never any reason for a transform to treat a ParenType
differently from the underlying type itself. Calling the given
function with ParenTypes is a source of bugs where they get
desugared on accident. Make transform() skip the function
entirely for ParenTypes, and remove a spot where we were
checking explicitly.
- In functions called from resolveType(), consistently
use a Type() return value to indicate 'unsatisfied
dependency', and ErrorType to indicate failure.
- Plumb the unsatisfiedDependency callback through the
resolution of the arguments of BoundGenericTypes, and
also pass down the options.
- Before doing a conformance check on the argument of a
BoundGenericType, kick off a TypeCheckSuperclass request
if the type in question is a class. This ensures we don't
recurse through NominalTypeDecl::prepareConformanceTable(),
which wants to see a class with a valid superclass.
- The ResolveTypeOfDecl request was assuming that
the request was satisfied after calling validateDecl().
This is not the case when the ITC is invoked from a
recursive call to validateDecl(), hack this up by returning
*true* from isResolveTypeDeclSatisfied(); otherwise we
assert in satisfy(), and we can't make forward progress
in this case anyway.
- Fix a bug in cycle breaking; it seems if we don't invoke
the cycle break callback on all pending requests, we end
up looping forever in an outer call to satisfy().
- Remove unused TR_GlobalTypeAlias option.
When called with a ParenType wrapping a DependentMemberType,
we would drop the ParenType because we used type->getAs<ParenType>()
rather than dyn_cast<ParenType>(type.getPointer()).
This fixes an existing QoI issue with closure argument tuples,
and prevents another regression once SubstitutedType is removed.
A pointless use of polymorphism -- the result values are not
interchangeable in any practical sense:
- For GenericTypeParamDecls, this returned getDeclaredInterfaceType(),
which is an interface type.
- For AssociatedTypeDecls, this returned the sugared AssociatedTypeType,
which desugars to an archetype.
- For TypeAliasDecls, this returned TypeAliasDecl::getAliasType(),
which desugars to a type containing archetypes.
- For NominalTypeDecls, this returned NominalTypeDecl::getDeclaredType(),
which is the unbound generic type, a special case used for inferring
generic arguments when they're not written in source.
Previously, getInterfaceType() would return getType() if no
interface type was set. Instead, always set an interface type
explicitly.
Eventually we want to remove getType() altogether, and this
brings us one step closer to this goal.
Note that ParamDecls are excempt from this treatment, because
they don't have a proper interface type yet. Cleaning this up
requires more effort.
Previously, getInterfaceType() would return getType() if no
interface type was set. Instead, always set an interface type
explicitly.
Eventually we want to remove getType() altogether, and this
brings us one step closer to this goal.
Note that ParamDecls are excempt from this treatment, because
they don't have a proper interface type yet. Cleaning this up
requires more effort.
Make SolverState manage whether the ConstraintSystem it belongs to has a
current SolverState.
Also a couple minor formatting fixes for ternary expressions involving
solverState.
This function had a weird, pre-ProtocolConformanceRef interface that
returned true when the type conformed to the protocol, then had a
separate indirect return value for the concrete conformance (if there
is one). Refactor this API, and the similar
TypeChecker::containsProtocol(), to produce an optional
ProtocolConformanceRef, which is far more idiomatic and easier to
use. Push ProtocolConformanceRef into a few more places. Should be NFC
The constraint solver tries not to solve for type variables that
"involve other type variables", which handles the case where we have
seen a constraint that mentions the type variable under consideration
as well as a different type variable, but in a constraint that we
cannot capture in a binding. Solving for such type variables too early
can lead to missed solutions, so we avoid it.
Tweak the logic for this computation to not consider type variables
mentioned within dependent member types (e.g., $T0.Iterator.Element),
because such types do not affect type inference at all, and therefore
shouldn't prevent solving for the type variable in question.
The ASTContext had a wacky "get member type" callback that actually
called back into the constraint system (!) to build member types. This
callback was obsoleted by the change that started representing nested
types as DependentMemberTypes.
In the constraint solver, we've traditionally modeled nested type via
a "type member" constraint of the form
$T1 = $T0.NameOfTypeMember
and treated $T1 as a type variable. While the solver did generally try
to avoid attempting bindings for $T1 (it would wait until $T0 was
bound, which solves the constraint), on occasion we would get weird
behavior because the solver did try to bind the type
variable.
With this commit, model nested types via DependentMemberType, the same
way we handle (e.g.) the nested type of a generic type parameter. This
solution maintains more information (e.g., we know specifically which
associated type we're referring to), fits in better with the type
system (we know how to deal with dependent members throughout the type
checker, AST, and so on), and is easier to reason able.
This change is a performance optimization for the type checker for a
few reasons. First, it reduces the number of type variables we need to
deal with significantly (we create half as many type variables while
type checking the standard library), and the solver scales poorly with
the number of type variables because it visits all of the
as-yet-unbound type variables at each solving step. Second, it
eliminates a number of redundant by-name lookups in cases where we
already know which associated type we want.
Overall, this change provides a 25% speedup when type-checking the
standard library.
When we process a constraint, the first step is generally to call
getFixedTypeRecursive() to look through type variables. When this
operation actually does non-trivial work, we could save
that result by considering the current constraint "solved" and
generating a new constraint (if needed!) with the simplified types.
This commit adds the infrastructure to do that, because it's important
when getFixedTypeRecursive() starts performing more interesting
substitutions (e.g., handling member types of type
variables). However, enabling for the common case of looking through a
type variable isn't profitable (it's ~2% slower to type-check the
standard library). Stage in this infrastructure change now.
Reimplement the witness matching logic used for generic requirements
so that it properly models the expectations required of the witness,
then captures the results in the AST. The new approach has a number of
advantages over the existing hacks:
* The constraint solver no longer requires hacks to try to tangle
together the innermost archetypes from the requirement with the
outer archetypes of the context of the protocol
conformance. Instead, we create a synthetic set of archetypes that
describes the requirement as it should be matched against
witnesses. This eliminates the infamous 'SelfTypeVar' hack.
* The type checker no longer records substitutions involving a weird
mix of archetypes from different contexts (see above), so it's
actually plausible to reason about the substitutions of a witness. A
new `Witness` class contains the declaration, substitutions, and all
other information required to interpret the witness.
* SILGen now uses the substitution information for witnesses when
building witness thunks, rather than computing all of it from
scratch. ``substSelfTypeIntoProtocolRequirementType()` is now gone
(absorbed into the type checker, and improved from there), and the
witness-thunk emission code is simpler. A few other bits of SILGen
got simpler because the substitutions can now be trusted.
* Witness matching and thunk generation involving generic requirements
and nested generics now works, based on some work @slavapestov was
already doing in this area.
* The AST verifier can now verify the archetypes that occur in witness substitutions.
* Although it's not in this commit, the `Witness` structure is
suitable for complete (de-)serialization, unlike the weird mix of
archetypes previously present.
Fixes rdar://problem/24079818 and cleans up an area that's been messy
and poorly understood for a very, very long time.
When performing the occurs check, look for the *representative* of the
type variable we're about to bind, rather than the type variable
itself. Fixes rdar://problem/26845038, SR-1512, SR-1902, SR2635,
SR-2852, and SR-2766.
We've been performing the "occurs" check when computing potential
bindings for type variables, but we weren't actually performing the
check for bindings that *must* occur. Perform the occurs check before
binding type variables, which fixes a few crashers and is far more principled.
Note that this obviates the need for tracking the type variables we've
substituted in simplifyType(), so simplify that as well.
Fixes rdar://problem/27879334 / SR-2351.
When we're creating a new constraint because we couldn't solve it (and
need to record the result), do so without trying to simplify it yet
again. It's just wasted work.
ConstraintSystem::addConstraint() is no longer used to simplify
existing constraints, nor does it have to deal with "externally
solved" constraints. Remove those parameters and simplify the code. NFC
When adding constraints into the constraint system, don't immediately
allocate a Constraint and add it via the most-general
addConstraint(). Instead, go through a more specific entrypoint (e.g.,
addValueMemberConstraint, addRestrictedConstraint, etc.), so we can
start phasing out the general "add an already-formed constraint"
function. NFC
There's no point in adding the constraints known on an associated type
to the constraint system, because they're implied by the protocol
constraint itself.
We had a few places that were performing ad hoc variants of
ConstraintSystem::getFixedTypeRecursive(); simplify it's interface so
we can use it everywhere consistently. Fixes rdar://problem/27261929.
Similar to “isTypeParameter,” this new entry point determines whether the type is a type variable or a nested type of a type thereof. The latter case isn’t actually formed yet, so this is NFC staging the trivial bits of this change.
In most places where we were checking "is<ErrorType>()", we now mean
"any error occurred". The few exceptions are in associated type
inference, code completion, and expression diagnostics, where we might
still work with partial errors.