Skip to content

Commit bf094b6

Browse files
committed
SIL: Fix some corner cases with tuple type lowering
1) It's possible to materialize a tuple value with an @escaping or @autoclosure element in it. I don't think this causes any bad behavior in 4.2 because these flags have no semantic effect after the type checker, but now I'm adding an assertion that will fire when such types are serialized, so let's make sure it doesn't happen by explicitly clearing out these flags when lowering tuples types. 2) It's also possible to materialize a tuple with a single vararg element. Again, this was not a problem in 4.2, but with the above change to start clearing tuple flags, we now end up in a situation where the lowered type is not a tuple, because TupleType::get() returns a ParenType if the tuple has one element that is not vararg (which it no longer is, because we just cleared all the flags). Fix the second problem by treating one-element vararg tuples just like tuples with inout, __shared and __owned elements, that is, by always exploding them when they appear at the top level of a function parameter list, ensuring we never try to materialize a value whose type is the entire tuple type. These problems all stem from the fact that lowering a function type with the opaque abstraction pattern treats the top level argument list as a single tuple argument. Once that is fixed, much of the above will simplify down to assertions.
1 parent 80a7ae1 commit bf094b6

File tree

6 files changed

+79
-27
lines changed

6 files changed

+79
-27
lines changed

include/swift/SIL/TypeLowering.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,29 @@ 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+
4467
/// The default convention for handling the callee object on thick
4568
/// callees.
4669
const ParameterConvention DefaultThickCalleeConvention =

lib/SIL/SILFunctionType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ class DestructureInputs {
607607
// a valid target for substitution, don't expand it.
608608
if (!tty ||
609609
(pattern.isTypeParameter() &&
610-
!tty->hasElementWithOwnership())) {
610+
!shouldExpandTupleType(tty))) {
611611
visit(paramFlags.getValueOwnership(), /*forSelf=*/false, pattern, ty,
612612
silRepresentation);
613613
return;

lib/SIL/TypeLowering.cpp

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,10 @@ 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));
13381342
assert(origType.matchesTuple(substType));
13391343

13401344
// Does the lowered tuple type differ from the substituted type in
@@ -1347,27 +1351,32 @@ static CanTupleType getLoweredTupleType(TypeConverter &tc,
13471351
auto origEltType = origType.getTupleElementType(i);
13481352
auto substEltType = substType.getElementType(i);
13491353

1350-
assert(!isa<LValueType>(substEltType) &&
1351-
"lvalue types cannot exist in function signatures");
1352-
assert(!isa<InOutType>(substEltType) &&
1353-
"inout cannot appear in tuple element type here");
1354-
1355-
// If the original type was an archetype, use that archetype as
1356-
// the original type of the element --- the actual archetype
1357-
// doesn't matter, just the abstraction pattern.
1354+
auto &substElt = substType->getElement(i);
1355+
1356+
// Make sure we don't have something non-materializable.
1357+
auto Flags = substElt.getParameterFlags();
1358+
assert(Flags.getValueOwnership() == ValueOwnership::Default);
1359+
13581360
SILType silType = tc.getLoweredType(origEltType, substEltType);
13591361
CanType loweredSubstEltType = silType.getASTType();
13601362

1361-
changed = (changed || substEltType != loweredSubstEltType);
1363+
changed = (changed || substEltType != loweredSubstEltType ||
1364+
!Flags.isNone());
13621365

1363-
auto &substElt = substType->getElement(i);
1364-
loweredElts.push_back(substElt.getWithType(loweredSubstEltType));
1366+
// Note: we drop any parameter flags such as @escaping, @autoclosure and
1367+
// varargs.
1368+
//
1369+
// FIXME: Replace this with an assertion that the original tuple element
1370+
// did not have any flags.
1371+
loweredElts.emplace_back(loweredSubstEltType,
1372+
substElt.getName(),
1373+
ParameterTypeFlags());
13651374
}
13661375

13671376
if (!changed) return substType;
13681377

1369-
// Because we're transforming an existing tuple, the weird corner
1370-
// case where TupleType::get does not return a TupleType can't happen.
1378+
// The cast should succeed, because if we end up with a one-element
1379+
// tuple type here, it must have a label.
13711380
return cast<TupleType>(CanType(TupleType::get(loweredElts, tc.Context)));
13721381
}
13731382

lib/SILGen/SILGenApply.cpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2448,10 +2448,12 @@ class ArgEmitter {
24482448
//
24492449
// FIXME: Once -swift-version 3 code generation goes away, we
24502450
// can simplify this.
2451-
if (isUnmaterializableTupleType(substArgType)) {
2452-
assert(origParamType.isTypeParameter());
2453-
emitExpanded(std::move(arg), origParamType);
2454-
return;
2451+
if (auto substTupleType = dyn_cast<TupleType>(substArgType)) {
2452+
if (shouldExpandTupleType(substTupleType)) {
2453+
assert(origParamType.isTypeParameter());
2454+
emitExpanded(std::move(arg), origParamType);
2455+
return;
2456+
}
24552457
}
24562458

24572459
// Okay, everything else will be passed as a single value, one
@@ -2538,13 +2540,6 @@ class ArgEmitter {
25382540
return (dest->isValid() ? dest : nullptr);
25392541
}
25402542

2541-
bool isUnmaterializableTupleType(CanType type) {
2542-
if (auto tuple = dyn_cast<TupleType>(type))
2543-
if (tuple->hasElementWithOwnership())
2544-
return true;
2545-
return false;
2546-
}
2547-
25482543
/// Emit an argument as an expanded tuple.
25492544
void emitExpanded(ArgumentSource &&arg, AbstractionPattern origParamType) {
25502545
assert(!arg.isLValue() && "argument is l-value but parameter is tuple?");

lib/SILGen/SILGenPoly.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -872,8 +872,9 @@ namespace {
872872
}
873873
}
874874

875-
// Special-case: tuples containing inouts, __shared or __owned.
876-
if (inputTupleType && inputTupleType->hasElementWithOwnership()) {
875+
// Special-case: tuples containing inouts, __shared or __owned,
876+
// and one-element vararg tuples.
877+
if (inputTupleType && shouldExpandTupleType(inputTupleType)) {
877878
// Non-materializable tuple types cannot be bound as generic
878879
// arguments, so none of the remaining transformations apply.
879880
// Instead, the outermost tuple layer is exploded, even when
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-emit-silgen -enable-sil-ownership %s | %FileCheck %s
2+
3+
public struct G<T> {
4+
var t: T
5+
6+
public init(t: T) { self.t = t }
7+
}
8+
9+
public func takesAutoclosureAndEscaping(_: @autoclosure () -> (), _: @escaping () -> ()) {}
10+
public func takesVarargs(_: Int...) {}
11+
12+
public func f() {
13+
_ = G(t: takesAutoclosureAndEscaping)
14+
_ = G(t: takesVarargs)
15+
}
16+
17+
// We shouldn't have @autoclosure and @escaping attributes in the lowered tuple type:
18+
19+
// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] @$SIg_Ieg_Iegyg_ytytIgnr__ytytIegnr_tytIegnr_TR : $@convention(thin) (@in_guaranteed (@noescape @callee_guaranteed (@in_guaranteed ()) -> @out (), @callee_guaranteed (@in_guaranteed ()) -> @out ()), @guaranteed @callee_guaranteed (@noescape @callee_guaranteed () -> (), @guaranteed @callee_guaranteed () -> ()) -> ()) -> @out ()
20+
21+
// The one-element vararg tuple ([Int]...) should be exploded and not treated as opaque,
22+
// even though its materializable:
23+
24+
// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] @$SSaySiGIegg_AAytIegnr_TR : $@convention(thin) (@in_guaranteed Array<Int>, @guaranteed @callee_guaranteed (@guaranteed Array<Int>) -> ()) -> @out ()

0 commit comments

Comments
 (0)