Skip to content

Commit 0241715

Browse files
committed
SIL: Stop imploding parameter list into a single value with opaque abstraction pattern
The constraint solver support for the Swift 3 function type behavior has been removed, so it's no longer possible to pun the same value as both a function taking multiple parameters and a function taking a single tuple argument. This means the entire parameter list is no longer a target for substitution as a single value, so the most general form of a function value passes each parameter indirectly instead of passing a single tuple parameter indirectly.
1 parent 8c94b38 commit 0241715

21 files changed

+120
-301
lines changed

include/swift/SIL/AbstractionPattern.h

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ namespace clang {
3434
namespace swift {
3535
namespace Lowering {
3636

37-
/// A pattern for the abstraction of a value. See the large comment
38-
/// in SILGenPoly.cpp.
37+
/// A pattern for the abstraction of a value.
3938
///
4039
/// The representation of values in Swift can vary according to how
4140
/// their type is abstracted: which is to say, according to the pattern
@@ -93,33 +92,21 @@ namespace Lowering {
9392
/// this representation when made abstract. Unfortunately, there
9493
/// are a lot of obvious situations where this is sub-optimal:
9594
/// for example, in totally non-generic code that just passes around
96-
/// a value of type (Int,Int)->Bool. It's particularly bad because
97-
/// Swift functions take multiple arguments as just a tuple, and that
98-
/// tuple is usually abstractable: e.g., '<' above could also be
99-
/// passed to this:
100-
/// func fred<T>(f : T -> Bool)
95+
/// a value of type (Int,Int)->Bool.
10196
///
10297
/// 3. Permit the representation of values to vary by abstraction.
10398
/// Values require coercion when changing abstraction patterns.
104-
/// For example, the argument to 'fred' would be expected to return
105-
/// its Bool result directly but take a single T parameter indirectly.
99+
/// For example, the argument to 'bar' would be expected to return
100+
/// its Bool result directly but take the T and U parameters indirectly.
106101
/// When '<' is passed to this, what must actually be passed is a
107-
/// thunk that expects a tuple of type (Int,Int) to be stored at
108-
/// the input address.
102+
/// thunk that loads both indirect parameters before calling '<'.
109103
///
110104
/// There is one major risk with (3): naively implemented, a single
111105
/// function value which undergoes many coercions could build up a
112106
/// linear number of re-abstraction thunks. However, this can be
113107
/// solved dynamically by applying thunks with a runtime function that
114108
/// can recognize and bypass its own previous handiwork.
115109
///
116-
/// There is one major exception to what sub-expressions in a type
117-
/// expression can be abstracted with type variables: a type substitution
118-
/// must always be materializable. For example:
119-
/// func f(inout Int, Int) -> Bool
120-
/// 'f' cannot be passed to 'foo' above: T=inout Int is not a legal
121-
/// substitution. Nor can it be passed to 'fred'.
122-
///
123110
/// In general, abstraction patterns are derived from some explicit
124111
/// type expression, such as the written type of a variable or
125112
/// parameter. This works whenever the expression directly provides
@@ -129,24 +116,25 @@ namespace Lowering {
129116
/// not provide structure at the appropriate level, i.e. when that
130117
/// level is substituted in: when the original type is merely T. In
131118
/// these cases, we must devolve to a representation which all legal
132-
/// substitutors will agree upon. In general, this is the
133-
/// representation of the type which replaces all materializable
134-
/// sub-expressions with a fresh type variable.
119+
/// substitutors will agree upon.
120+
///
121+
/// The most general type of a function type replaces all parameters and the
122+
/// result with fresh, unrestricted generic parameters.
123+
///
124+
/// That is, if we have a substituted function type:
125+
///
126+
/// (UnicodeScalar, (Int, Float), Double) -> (Bool, String)
127+
///
128+
/// then its most general form is
135129
///
136-
/// For example, when applying the substitution
137-
/// T=(Int,Int)->Bool
138-
/// values of T are abstracted as if they were of type U->V, i.e.
139-
/// taking one indirect parameter and returning one indirect result.
130+
/// (A, B, C) -> D
140131
///
141-
/// But under the substitution
142-
/// T=(inout Int,Int)->Bool
143-
/// values of T are abstracted as if they were of type (inout U,V)->W,
144-
/// i.e. taking one parameter inout, another indirectly, and returning
145-
/// one indirect result.
132+
/// because there is a valid substitution
133+
/// A := UnicodeScalar
134+
/// B := (Int, Float)
135+
/// C := Double
136+
/// D := (Bool, String)
146137
///
147-
/// An abstraction pattern is represented with an original,
148-
/// unsubstituted type. The archetypes or generic parameters
149-
/// naturally fall at exactly the specified abstraction points.
150138
class AbstractionPattern {
151139
enum class Kind {
152140
/// A type reference. OrigType is valid.

include/swift/SIL/TypeLowering.h

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,43 +41,6 @@ namespace swift {
4141

4242
namespace Lowering {
4343

44-
/// Should this tuple type always be expanded into its elements, even
45-
/// when emitted against an opaque abstraction pattern?
46-
///
47-
/// FIXME: Remove this once function signature lowering always explodes
48-
/// the top-level argument list.
49-
inline bool shouldExpandTupleType(TupleType *type) {
50-
// Tuples with inout, __shared and __owned elements cannot be lowered
51-
// to SIL types.
52-
if (type->hasElementWithOwnership())
53-
return true;
54-
55-
// A one-element tuple with a vararg element is essentially
56-
// equivalent to the element itself, and we also can't lower it, since
57-
// that would strip off the vararg-ness and produce a non-tuple type.
58-
if (type->getNumElements() == 1 &&
59-
type->getElement(0).isVararg()) {
60-
return true;
61-
}
62-
63-
// Everything else is OK.
64-
return false;
65-
}
66-
67-
/// A version of the above for parameter lists.
68-
///
69-
/// FIXME: Should also remove this soon.
70-
inline bool shouldExpandParams(AnyFunctionType::CanParamArrayRef params) {
71-
for (auto param : params)
72-
if (param.getValueOwnership() != ValueOwnership::Default)
73-
return true;
74-
75-
if (params.size() == 1)
76-
return params[0].isVariadic();
77-
78-
return false;
79-
}
80-
8144
/// The default convention for handling the callee object on thick
8245
/// callees.
8346
const ParameterConvention DefaultThickCalleeConvention =

lib/SIL/SILFunctionType.cpp

Lines changed: 7 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -477,70 +477,15 @@ static bool isFormallyPassedIndirectly(SILModule &M,
477477
}
478478
}
479479

480-
/// A visitor for turning formal input types into SILParameterInfos,
481-
/// matching the abstraction patterns of the original type.
482-
///
483-
/// If the original abstraction pattern is fully opaque, we must
484-
/// pass the function's inputs as if the original type were the most
485-
/// general function signature (expressed entirely in type
486-
/// variables) which can be substituted to equal the given
487-
/// signature.
488-
///
489-
/// The goal of the most general type is to be (1) unambiguous to
490-
/// compute from the substituted type and (2) the same for every
491-
/// possible generalization of that type. For example, suppose we
492-
/// have a Vector<(Int,Int)->Bool>. Obviously, we would prefer to
493-
/// store optimal function pointers directly in this array; and if
494-
/// all uses of it are ungeneralized, we'd get away with that. But
495-
/// suppose the vector is passed to a function like this:
496-
/// func satisfiesAll<T>(v : Vector<(T,T)->Bool>, x : T, y : T) -> Bool
497-
/// That function will expect to be able to pull values out with the
498-
/// proper abstraction. The only type we can possibly expect to agree
499-
/// upon is the most general form.
500-
///
501-
/// The precise way this works is that Vector's subscript operation
502-
/// (assuming that's how it's being accessed) has this signature:
503-
/// <X> Vector<X> -> Int -> X
504-
/// which 'satisfiesAll' is calling with this substitution:
505-
/// X := (T, T) -> Bool
506-
/// Since 'satisfiesAll' has a function type substituting for an
507-
/// unrestricted archetype, it expects the value returned to have the
508-
/// most general possible form 'A -> B', which it will need to
509-
/// de-generalize (by thunking) if it needs to pass it around as
510-
/// a '(T, T) -> Bool' value.
511-
///
512-
/// It is only this sort of direct substitution in types that forces
513-
/// the most general possible type to be selected; declarations will
514-
/// generally provide a target generalization level. For example,
515-
/// in a Vector<IntPredicate>, where IntPredicate is a struct (not a
516-
/// tuple) with one field of type (Int, Int) -> Bool, all the
517-
/// function pointers will be stored ungeneralized. Of course, such
518-
/// a vector couldn't be passed to 'satisfiesAll'.
519-
///
520-
/// For most types, the most general type is simply a fresh,
521-
/// unrestricted type variable. But unmaterializable types are not
522-
/// valid results of substitutions, so this does not apply. The
523-
/// most general form of an unmaterializable type preserves the
524-
/// basic structure of the unmaterializable components, replacing
525-
/// any materializable components with fresh type variables.
480+
/// A visitor for turning formal input types into SILParameterInfos, matching
481+
/// the abstraction patterns of the original type.
526482
///
527-
/// That is, if we have a substituted function type:
528-
/// (UnicodeScalar, (Int, Float), Double) -> Bool
529-
/// then its most general form is
530-
/// A -> B
483+
/// If the original abstraction pattern is fully opaque, we must pass the
484+
/// function's parameters and results indirectly, as if the original type were
485+
/// the most general function signature (expressed entirely in generic
486+
/// parameters) which can be substituted to equal the given signature.
531487
///
532-
/// because there is a valid substitution
533-
/// A := (UnicodeScalar, (Int, Float), Double)
534-
/// B := Bool
535-
///
536-
/// But if we have a substituted function type:
537-
/// (UnicodeScalar, (Int, Float), inout Double) -> Bool
538-
/// then its most general form is
539-
/// (A, B, inout C) -> D
540-
/// because the substitution
541-
/// X := (UnicodeScalar, (Int, Float), inout Double)
542-
/// is invalid substitution, ultimately because 'inout Double'
543-
/// is not materializable.
488+
/// See the comment in AbstractionPattern.h for details.
544489
class DestructureInputs {
545490
SILModule &M;
546491
const Conventions &Convs;
@@ -603,21 +548,6 @@ class DestructureInputs {
603548
// Add any leading foreign parameters.
604549
maybeAddForeignParameters();
605550

606-
// FIXME(swift3): Remove this.
607-
//
608-
// If the abstraction pattern is opaque and the parameter list is a valid
609-
// target for substitution, implode it into a single tuple parameter.
610-
if (!hasSelf) {
611-
if (origType.isTypeParameter() && !shouldExpandParams(params)) {
612-
CanType ty = AnyFunctionType::composeInput(M.getASTContext(), params,
613-
/*canonicalVararg*/true)
614-
->getCanonicalType();
615-
visit(ValueOwnership::Default, /*forSelf=*/false,
616-
origType, ty, silRepresentation);
617-
return;
618-
}
619-
}
620-
621551
// Process all the non-self parameters.
622552
for (unsigned i = 0; i != numNonSelfParams; ++i) {
623553
auto ty = params[i].getParameterType();

lib/SIL/TypeLowering.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,10 +1335,6 @@ void TypeConverter::insert(TypeKey k, const TypeLowering *tl) {
13351335
static CanTupleType getLoweredTupleType(TypeConverter &tc,
13361336
AbstractionPattern origType,
13371337
CanTupleType substType) {
1338-
// We can't lower InOutType, and we can't lower an unlabeled one
1339-
// element vararg tuple either, because lowering strips off flags,
1340-
// which would end up producing a ParenType.
1341-
assert(!shouldExpandTupleType(substType));
13421338
assert(origType.matchesTuple(substType));
13431339

13441340
// Does the lowered tuple type differ from the substituted type in
@@ -1356,23 +1352,24 @@ static CanTupleType getLoweredTupleType(TypeConverter &tc,
13561352
// Make sure we don't have something non-materializable.
13571353
auto Flags = substElt.getParameterFlags();
13581354
assert(Flags.getValueOwnership() == ValueOwnership::Default);
1355+
assert(!Flags.isVariadic());
13591356

13601357
SILType silType = tc.getLoweredType(origEltType, substEltType);
13611358
CanType loweredSubstEltType = silType.getASTType();
13621359

13631360
changed = (changed || substEltType != loweredSubstEltType ||
13641361
!Flags.isNone());
13651362

1366-
// Note: we drop any parameter flags such as @escaping, @autoclosure and
1367-
// varargs.
1363+
// Note: we drop @escaping and @autoclosure which can still appear on
1364+
// materializable tuple types.
13681365
//
13691366
// FIXME: Replace this with an assertion that the original tuple element
13701367
// did not have any flags.
13711368
loweredElts.emplace_back(loweredSubstEltType,
13721369
substElt.getName(),
13731370
ParameterTypeFlags());
13741371
}
1375-
1372+
13761373
if (!changed) return substType;
13771374

13781375
// The cast should succeed, because if we end up with a one-element

lib/SILGen/SILGenApply.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,13 +1712,11 @@ static unsigned getFlattenedValueCount(AbstractionPattern origType,
17121712

17131713
// The count is always 1 unless the substituted type is a tuple.
17141714
auto substTuple = dyn_cast<TupleType>(substType);
1715-
if (!substTuple) return 1;
1715+
if (!substTuple)
1716+
return 1;
17161717

1717-
// If the original type is opaque and the substituted type is
1718-
// materializable, the count is 1 anyway.
1719-
//
1720-
// FIXME: Should always be materializable here.
1721-
if (origType.isTypeParameter() && substTuple->isMaterializable())
1718+
// If the original type is opaque, the count is 1 anyway.
1719+
if (origType.isTypeParameter())
17221720
return 1;
17231721

17241722
// Otherwise, add up the elements.

0 commit comments

Comments
 (0)