When this was migrated over to the awesome new special representation
of type(of:) the argument coercion didn’t come with it. The missing
load expression caused any l-value run through type(of:) to crash in the
verifier.
If a convenience initializer in a subclass delegated to an inherited initializer from its base, we would end up type-checking the reference to the base class constructor as returning the base type, leading to type mismatches in the result AST and downstream crashes. We can wrap up the synthesized OtherConstructorRefExpr in a CovariantFunctionConversionExpr, which will trick the type checker into propagating the covariant result that gets rebound to `self` on return, avoiding this problem. (For now, I'm avoiding making the constructor decl formally have a Self return type, since that exposes a bunch of downstream breakage in code paths that only expect FuncDecls to be covariant, and also affects the mangling of constructors, causing a bunch of test case thrash we really don't want to inflict on the 3.1 branch.)
A new SubstitutionMap::getProtocolSubstitutions() method handles
the case where we construct a trivial SubstitutionMap to replace
the protocol Self type with a concrete type.
When substituting one opened existential archetype for another,
use the form of Type::subst() that takes two callbacks instead of
building a SubstitutionMap. SubstitutionMaps are intended to be
used with keys that either come from a GenericSignature or a
GenericEnvironment, so using them to replace opened archetypes
doesn't fit the conceptual model we're going for.
Another pile of changes to use a side map for types in the constraint
solver and only write them directly into expressions once we have a
known good solution that we want to apply.
Still incomplete, we continue to write the types into expressions along
the way at the moment.
First, add some new utility methods to create SubstitutionMaps:
- GenericSignature::getSubstitutionMap() -- provides a new
way to directly build a SubstitutionMap. It takes a
TypeSubstitutionFn and LookupConformanceFn. This is
equivalent to first calling getSubstitutions() with the two
functions to create an ArrayRef<Substitution>, followed by
the old form of getSubstitutionMap() on the result.
- TypeBase::getContextSubstitutionMap() -- replacement for
getContextSubstitutions(), returning a SubstitutionMap.
- TypeBase::getMemberSubstitutionMap() -- replacement for
getMemberSubstitutions(), returning a SubstitutionMap.
With these in place, almost all existing uses of subst() taking
a ModuleDecl can now use the new form taking a SubstitutionMap
instead. The few remaining cases are explicitly written to use a
TypeSubstitutionFn and LookupConformanceFn.
Constraint generation for interpolated string literals was
meticulously producing a constraint system that included all of the
generated calls to init(stringInterpolationSegment:). While accurate,
this is completely unnecessary (because the
ExpressibleByStringInterpolation protocol allows *anything* to be
interpolated) and leads to large, intertwined constraint systems
where:
(1) There are two additional type variables per string interpolation
segment, and
(2) Those type variables form a bridge between the string
interpolation's type variable and the type variables of each string
interpolation segment, and
(3) init(stringInterpolationSegment:) tends to be overloaded (4
overloads, down from 17 due to
29353013c0)
which left each string interpolation as a large connected component
with big disjunctions.
Drop the calls to init(stringInterpolationSegment:) from the
constraint system. This eliminates two type
variables per segment (due to (1) going away), and breaks the bridge
described in (2), so that each string interpolation segment is
treated as an separate connected component and, therefore, will be
solved independently. The actual resolution of
init(stringInterpolationSegment:) overloads is pushed to the
constraint application phase, where we are only dealing with concrete
types and *much* smaller constraint systems.
Fixes rdar://problem/29389887 more thoroughly.
When I refactored the handling of bridging conversions (e.g.,
valueType as? NSClassType), I broke the path that performed a bridging
conversion followed by a conversion to an existential, e.g.,
"some-bridged-value-type as CVarArg". Reinstate such use cases.
Fixes rdar://problem/30195862.
Without this, CSGen/CSSimplify and CSApply may have differing
opinions about whether e.g. a let property is settable, which
can lead to invalid ASTs.
Arguably, a better fix would be to remove the dependency on the
exact nested DC. For example, we could treat lets as settable
in all contexts and then just complain later about invalid
attempts to set them. Or we could change CSApply to directly
use the information it already has about how an l-value is used,
rather than trying to figure out whether it *might* be getting set.
But somehow, tracking a new piece of information through the
entire constraint system seems to be the more minimal change.
Fixes rdar://29810997.
Value type initializers must initialize stored properties directly
if they do not delegate to another initializer via self.init().
Since direct stored property access is not permitted for resilient
value types from outside their resilience domain, this means that
such initializers are prohibited in two cases:
- If the initializer is defined in an extension from outside the
value type's resilience domain
- If the initializer is public and @_inlineable, since it might get
inlined outside the value type's resilience domain
Right now, such initializers cannot *assign* to self either;
I filed <https://bugs.swift.org/browse/SR-3686> to track the issue.
ConstraintSystem::simplifyType() replaced types with their fixed types
from the global map.
Solution::simplifyType() replaced types with their fixed types from the
current bindings.
There were some minor differences between the two, and some dead code.
Constraint application had a "peephole" to try to generate a series of
optional-injection expressions when performing an optional-to-optional
coercion such as "Int??!" to "Int?????!". However, the computation
incorrectly selecting between implicitly-unwrapped optional and
optional types for intermediate steps, leading to an AST verification
failure. Clean up the implementation to use
TypeBase::lookThroughAllAnyOptionalTypes() rather than hand-rolled,
more-lossy versions and use the actual optional types in the "to" type
along each step, which has the side effect of maintaining type sugar.
In Swift 4 mode, no longer consider e.g. 'nsNumber as Int' or 'nsValue as NSRange' to be valid coercions. This would break compatibility with Swift 3, so in Swift 3 mode, accept the coercion, but *also* accept a checked cast without a warning, and raise a migration warning about the unchecked coercion.
Several fixes in order to make handling of types more consistent.
In particular:
- Expression types used within the constraint system itself always use
the type map.
- Prior to calling out to any TypeChecker functions, the types are
written back into the expression tree. After the call, the types are
read back into the constraint system type map.
- Some calls to directly set types on the expressions are reintroduced
in places they make sense (e.g. when building up new expressions that
are then passed to typeCheckExpressionShallow).
ConstraintSystem::setType() is still setting the type on the expression
nodes at the moment, because there is still some incorrect handling of
types that I need to track down (in particular some issues related to
closures do not appear to be handled correctly). Similarly, when we
cache the types in the constraint system map, we are not clearing them
from the expression tree yet.
The diagnostics have not been updated at all to use the types from
the constraint system where appropriate, and that will need to happen as
well before we can remove the write from ConstraintSystem::setType()
into the expression and enable the clearing of the expression tree type
when we cache it in the constraint system.
Previously this would cause strange diagnostics to be emitted when
values of type Bool! were the subject of if-expression conditions.
var isBoolean: Bool! = false
let conditional = isBoolean ? "Broken" : "Heart"
// ^ type 'Bool' is broken
Instead, look through IOUs before performing the member access to cover
our bases here.
These are used from within constraint system code, and for those uses we
need to be reading from the constraint system type map.
Add the parallel constraint system interfaces that call into the
Expr interfaces with the appropriate accessors.
- TypeMatchVisitor doesn't like seeing ErrorTypes -- stop if we have one.
- Common logic for replacing type parameters and variables with
UnresolvedType.
- Fix crash in coerceToType() if one of the two types has an UnresolvedType
in it, but not at the top level.
Fixes one compiler_crasher and some regressions with upcoming patches.
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>.
When we have a value 'p' of type 'P.Type' for some protocol 'P',
we require the user to write 'p.init()' rather than 'p()' to
construct an instance of a concrete type conforming to 'P'.
If they write 'p()', we suggest a fixit to insert '.init',
however if there is also a subsequent member access on the
result, for example 'p().foo', we would crash.
Another invalid case that used to crash is when you construct
a protocol metatype directly and then access a member of it,
eg. 'P().foo'.
Force unwrapping the expression and propagating that type down to the
rest of the tree causes crashes when we go to request a different set
of protocols than we were expecting from it later. Make this
transformation local to the apply instead.
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.
We were dropping the substitutions required to call the default
argument generator on the floor, when the correct solution is to
remap them to substitutions in terms of the base class signature.
Fixes <rdar://problem/29721571>.
The prior formulation of bridging conversions allowed conversion to
more-optional types, e.g., converting an "NSDate" to "Date?", which
was broken by my recent refactoring in this area. Allow bridging
conversions to more-optional types by introducing extra optional
injections at the end.
Fixes rdar://problem/29780527.
Specialize and improve the "downcast only unwraps optionals"
diagnostic to provide specific diagnostics + Fix-Its for the various
casts of forced cast, conditional cast, and "isa" check. Specifically:
* With a forced cast, customize the diagnostic. We still insert the
appropriate number of !'s, but now we remove the 'as! T' (if an
implicit conversion would suffice) or replace the 'as!' with 'as'
(if we still need a bridge)
* With a conditional cast, only emit a diagnostic if we're removing
just one level of optional. In such cases, we either have a no-op
(an implicit conversion would do) or we could just use 'as' to the
optional type, so emit a customized warning to do that. If we are
removing more than one level of optional, don't complain:
conditional casts can remove optionals. Add the appropriate Fix-Its
here.
* With an 'is' expression, only emit a diagnostic if we're removing
just one level of optional. In this case, the 'is' check is
equivalent to '!= nil'. Add a Fix-It for that.
Across the board, reduce the error to a warning. These are
semantically-well-formed casts, it's just that they could be written
better.
Fixes rdar://problem/28856049 and rdar://problem/22275685.
The type checker implements logic for handling checked casts in two
places: the constraint solver (for type-checking expressions
containing "as!" or "as?") and as a top-level entrypoint for
type-checking as?/as! for diagnostics and is/as patterns. Needless to
say, the two implementations were inconsistent, and in fact both were
wrong, leading to various problems---rejecting perfectly-valid "as!"
and "as?" casts outright, bogus warnings that particular as!/as? casts
always-succeed or always-fail when they wouldn't, and so on.
Start detangling the mess in two ways. First, drastically simplify the
handling of checked casts in the constraint solver, eliminating the
unprincipled "subtype" constraint checks that (among other things)
broke the handling of checked casts that involved bridging or optional
unwrapping. The simpler code is more permissive and more correct; it
essentially accepts that the user knows what she is doing with the
cast.
Second, make the type checker's checking of casts far more thorough,
which includes:
* When we're performing a collection cast, actually check that the
element types (and key types, for a dictionary) are castable, rather
than assuming all collection casts are legitimate. This means we'll
get more useful "always fails" and "always succeeds" diagnostics for
array/set/dictionary.
* Handle casts from a bridged value type to a subclass of the
corresponding bridged class type. Previously, these would be
incorrectly classified as "always fails".
While I'm here, eliminate a spurious diagnostic that occurs when using
a conditional cast ("as?") that could have been a coercion/bridging
conversion ("as"). The optional injection we synthesize to get the
resulting type correct was getting diagnosed as an implicit coercion,
but shouldn't have been.
Previously, bridging conversions were handled as a form of "explicit
conversion" that was treated along the same path as normal
conversions in matchTypes(). Historically, this made some
sense---bridging was just another form of conversion---however, Swift
now separates out bridging into a different kind of conversion that is
available only via an explicit "as". This change accomplishes a few
things:
* Improves type inference around "as" coercions. We were incorrectly
inferring type variables of the "x" in "x as T" in cases where a
bridging conversion was expected, which cause some type inference
failures (e.g., the SR-3319 regression).
* Detangles checking for bridging conversions from other conversions,
so it's easier to isolate when we're applying a bridging
conversion.
* Explicitly handle optionals when dealing with bridging conversions,
addressing a number of problems with incorrect diagnostics, e.g.,
complains about "unrelated type" cast failures that would succeed at
runtime.
Addresses rdar://problem/29496775 / SR-3319 / SR-2365.