Add type checker performance tests under
validation-test/Sema/type_checker_perf.
Under ./fast/ we have tests that compile reasonably quickly now but at
one point did not.
Under ./slow/ we have tests that are still very slow to compile.
Some tests use %scale-test and others
-solver-expression-time-threshold to determine if they are scaling
well or compiling fast enough.
I've got several more tests gathered that have not yet been set up to
run in our test system. Those are forthcoming!
Further contributions welcome.
Thanks go to @xedin who helped collect most of the test cases.
Special DeclNames represent names that do not have an identifier in the
surface language. This implies serializing the information about whether
a name is special together with its identifier (if it is not special)
in both the module file and the swift lookup table.
There are possible situations when we find solutions with String
and String -> UnsafePointer conversions at the same time for
expressions with default string literals. In order to disambiguite
such situations let's prefer solutions without String -> UnsafePointer
conversions if possible.
Restrict skipping of the generic overloads only to the situations
when non-generic solution doesn't have any restrictions/fixes, because
there is a possibility that generic overload could produce a better
solution.
Resolves: rdar://problem/32204609.
This reverts commit
ecfa406fc5, which was reinstating
3c5b393e0c.
It does not revert one test change from that commit, because
inexplicably one of the tests is still failing, probably due to some
other changes that have happened since. I'm leaving a ticket open to
investigate.
I've added one of the new cases that is failing as a result of this
change.
This is being reverted not because of bugs in this particular commit,
but because it seems to be exposing other bugs in the type checker that
are resulting in source compatibility problems. We need to shake these
other bugs out of the type checker before bringing this change back.
We previously used a simple heuristic of visiting the disjunction with
the fewest number of elements in it.
Instead, this commit introduces a new way to select the best disjunction
to explore based on attempting disjunctions based on either how much
information we have about the associated type variables (e.g. how many
of the arguments of a function call in the case of bind overload
disjunctions) or by other criteria that help constraint the search or
split the connected components (e.g. coercions and calls to
initializers).
A key part of the improvement here is allowing the type checker to
attempt bindings of types to type variables when there are still
argument conversion constraints between type variables in the
system. The insight here is if there are no other constraints blocking
an attempt to bind types and we have types to try, we will be able to
test those types against active conformance constraints and potentially
fail much earlier while solving.
Visiting disjunctions in a different order exposed some other problems
with the type checker including a couple cases where map is used that
are now considered ambiguous due to problems in how we are ranking
solutions.
I measured an 11-13% reduction in type checking time for the standard
library using a release build of the compiler.
The constraint graph models type variables (as the nodes) and
constraints (as the multi-edges connecting nodes). The connected
components within this (multi-)graph are independent subproblems that
are solved separately; the results from each subproblem are then
combined. The approach helps curtail exponential behavior, because
(e.g.) the disjunctions/type variables in one component won't ever be
explored while solving for another component
This approach assumes that all of the constraints that cannot be
immediately solved are associated with one or more type
variables. This is almost entirely true---constraints that don't
involve type variables are immediately simplified.
Except for disjunctions. A disjunction involving no type variables
would not appear *at all* in the constraint graph. Worse, it's
independence from other constraints could not be established, so the
constraint solver would go exponential for every one of these
constraints. This has always been an issue, but it got worse with the
separation of type checking of "as" into the "coercion" case and the
"bridging" case, which introduced more of these disjunctions. This led
to counterintuitive behavior where adding "as Foo" would cause the
type checking to take *more* time than leaving it off, if both sides
of the "as" were known to be concrete. rdar://problem/30545483
captures a case (now in the new test case) where we saw such
exponential blow-ups.
Teach the constraint graph to keep track of "orphaned" constraints
that don't reference any type variables, and treat each "orphaned"
constraint as a separate connected component. That way, they're solved
independently.
Fixes rdar://problem/30545483 and will likely curtain other
exponential behavior we're seeing in the solver.
Remove 16 concrete init(stringInterpolationSegment:) overloads and
replace them with 3 generic overloads, significantly reducing the
exponential blow-up from larger string interpolations.
Fixes rdar://problem/29389887.
...avoiding a crash when trying to detect near misses of protocol
requirements. Unfortunately I can't come up with a test case for the
VarDecl changes; everything I try seems to already work. But using
interface types is more correct anyway.
https://bugs.swift.org/browse/SR-3812
Remove 16 concrete init(stringInterpolationSegment:) overloads and
replace them with 3 generic overloads, significantly reducing the
exponential blow-up from larger string interpolations.
Fixes rdar://problem/29389887.
Swift 3.0 allowed constructing an enum or calling a function-typed
property with multiple arguments even when a single argument of tuple
type was expected. Emulate that in Swift 3 mode by wrapping in an
extra level of parentheses when the situation comes up.
Last vestiges of fallout from SE-0110. Hopefully last, anyway. A nice
follow-up to this commit might be to /warn/ in Swift 3 mode when this
happens.
rdar://problem/30171399
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>.
The old diagnostic was misleading; the closure really does take
one argument, but we're _returning_ a value that doesn't match
the contextual return type. And the suggestion to wrap the
argument in parens makes no sense either.
The new diagnostic is totally useless, but at least isn't not
misleading.
Some of the type members might be unresolved because they are misspelled or
do not exist (represented by DependentMemberType), so when trying to add
requirements to the nested types don't expect that all of the members have
their associated types resolved.
Resolves: <rdar://problem/28235248>.
If a sugared type desugars to a substitutable type, we would
return the replacement type without the sugar. I think in
practice this meant that ParenType would be lost sometimes.
Preserving this correctly is required for an upcoming CSDiag
change.
Note that there's a minor source-breaking change with enum
case constructors here. I've filed <rdar://problem/27261929>
to track sorting it out in Swift 3 mode.
Also an upcoming patch fixes another related issue and adds more
tests for case constructors.
Add a case to ExprRewriter.coerceToType which tries to look through
ImplicitlyUnwrappedOptional<T> and apply 'to-value' transformation
before coercing to required 'to' type.
Resolves: <rdar://problem/28023899>.
This handles situation when overload for the subscript hasn't been resolved
by constraint solver, such might happen, for example, if solver was allowed to
produce solutions with free or unresolved type variables (e.g. when running diagnostics).
Resolves: <rdar://problem/27329076>, <rdar://problem/28619118>, <rdar://problem/2778734>.
When trying to convert tuple type to existential look through
it's elements and convert found LValues to RValues (via load)
before applying erasure.
Resolves: <rdar://problem/27575060>.
It is possible to have requirement environments in which substitution
of the conforming type for Self into the requirement's signature would
result in substituted same-type requirements that no longer involve
type parameters. This triggered an assertion in the construction of
the requiremet environement; instead, just drop the requirement
because it is no longer interesting. Witness matching will simply fail
later one.
With this fix, it now because possible to add generic requirements to
a protocol that were unsatisfiable for certain models. For example,
the following protocol:
protocol P {
associatedtype A
associatedtype B
func f<T: P>(_: T) where T.A == Self.A, T.A == Self.B
}
can only be satisfied by conforming types for which Self.A ==
Self.B. SE-0142 will introduce a proper way to add such requirements
onto associated types. This commit makes any such attempt to add
requirements onto "Self" (or its associated types) ill-formed, so we
will reject the protocol P above with a diagnostic such as:
error: instance method requirement 'f' cannot add constraint
'Self.A == Self.B' on 'Self'
Fixes rdar://problem/29075927.
When cast expressions (conditional or forced downcasts) are part
of the closure expression with invalid parameters or return type,
they are not going be to folded by PreCheckExpression, which means
that they are not going to have sub-expression (from) set.
Resolves: <rdar://problem/27464577>.
Disallows solutions containing free type variables on the final step of
witness matching, because that would yield incorrect results when witness
type has free type variables as well.
Resolves: <rdar://problem/27249691>.
Modify TypeChecker::coerceParameterListToType to always validate and consider only
valid contextual types (contains: no undefined, error, or type variables etc.) for
argument type coercion, such logic prevents erasure of important explicitly specified
type information attached to parameters of the closure expressions being diagnosed.
Resolves: SR-2994.
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.
In cases where we cannot infer the types they won't be, so we don't want
to just cast to BoundGenericType when we see these.
Fixes rdar://problem/28317710 and at least one dup (and I think a few
more).
When a constraint fails, we retire it... but we also need to remove it
from the constraint graph. Otherwise, we break invariants when
diagnostic generation attempts to continue simplification.
Fixes rdar://rdar28145033.
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.
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.