Skip to content

Commit 59eae1e

Browse files
authored
Merge pull request #18659 from slavapestov/sil-lowering-illegal-tuples
SIL: Don't try to lower various illegal tuple types
2 parents 3f4227e + bf094b6 commit 59eae1e

File tree

9 files changed

+119
-53
lines changed

9 files changed

+119
-53
lines changed

include/swift/AST/Types.h

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,9 @@ class alignas(1 << TypeAlignInBits) TypeBase {
361361
);
362362

363363
SWIFT_INLINE_BITFIELD_FULL(TupleType, TypeBase, 1+32,
364-
/// Whether an element of the tuple is inout.
365-
HasInOutElement : 1,
364+
/// Whether an element of the tuple is inout, __shared or __owned.
365+
/// Values cannot have such tuple types in the language.
366+
HasElementWithOwnership : 1,
366367

367368
: NumPadBits,
368369

@@ -1876,9 +1877,9 @@ class TupleType final : public TypeBase, public llvm::FoldingSetNode,
18761877
/// return the element index, otherwise return -1.
18771878
int getNamedElementId(Identifier I) const;
18781879

1879-
/// Returns true if this tuple has inout elements.
1880-
bool hasInOutElement() const {
1881-
return static_cast<bool>(Bits.TupleType.HasInOutElement);
1880+
/// Returns true if this tuple has inout, __shared or __owned elements.
1881+
bool hasElementWithOwnership() const {
1882+
return static_cast<bool>(Bits.TupleType.HasElementWithOwnership);
18821883
}
18831884

18841885
// Implement isa/cast/dyncast/etc.
@@ -1895,9 +1896,9 @@ class TupleType final : public TypeBase, public llvm::FoldingSetNode,
18951896
private:
18961897
TupleType(ArrayRef<TupleTypeElt> elements, const ASTContext *CanCtx,
18971898
RecursiveTypeProperties properties,
1898-
bool hasInOut)
1899+
bool hasElementWithOwnership)
18991900
: TypeBase(TypeKind::Tuple, CanCtx, properties) {
1900-
Bits.TupleType.HasInOutElement = hasInOut;
1901+
Bits.TupleType.HasElementWithOwnership = hasElementWithOwnership;
19011902
Bits.TupleType.Count = elements.size();
19021903
std::uninitialized_copy(elements.begin(), elements.end(),
19031904
getTrailingObjects<TupleTypeElt>());
@@ -4980,13 +4981,13 @@ inline bool TypeBase::isTypeParameter() {
49804981
inline bool TypeBase::isMaterializable() {
49814982
if (hasLValueType())
49824983
return false;
4983-
4984+
49844985
if (is<InOutType>())
49854986
return false;
4986-
4987+
49874988
if (auto *TTy = getAs<TupleType>())
4988-
return !TTy->hasInOutElement();
4989-
4989+
return !TTy->hasElementWithOwnership();
4990+
49904991
return true;
49914992
}
49924993

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/AST/ASTContext.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2974,7 +2974,7 @@ Type TupleType::get(ArrayRef<TupleTypeElt> Fields, const ASTContext &C) {
29742974
Fields[0].getParameterFlags());
29752975

29762976
RecursiveTypeProperties properties;
2977-
bool hasInOut = false;
2977+
bool hasElementWithOwnership = false;
29782978
for (const TupleTypeElt &Elt : Fields) {
29792979
auto eltTy = Elt.getType();
29802980
if (!eltTy) continue;
@@ -2984,11 +2984,13 @@ Type TupleType::get(ArrayRef<TupleTypeElt> Fields, const ASTContext &C) {
29842984
// non-paren tuples are malformed and will be diagnosed later.
29852985
if (auto *TTy = Elt.getType()->getAs<TupleType>()) {
29862986
if (TTy->getNumElements() == 1)
2987-
hasInOut |= TTy->hasInOutElement();
2987+
hasElementWithOwnership |= TTy->hasElementWithOwnership();
29882988
} else if (auto *Pty = dyn_cast<ParenType>(Elt.getType().getPointer())) {
2989-
hasInOut |= Pty->getParameterFlags().isInOut();
2989+
hasElementWithOwnership |= (Pty->getParameterFlags().getValueOwnership() !=
2990+
ValueOwnership::Default);
29902991
} else {
2991-
hasInOut |= Elt.getParameterFlags().isInOut();
2992+
hasElementWithOwnership |= (Elt.getParameterFlags().getValueOwnership() !=
2993+
ValueOwnership::Default);
29922994
}
29932995
}
29942996

@@ -3016,7 +3018,7 @@ Type TupleType::get(ArrayRef<TupleTypeElt> Fields, const ASTContext &C) {
30163018
sizeof(TupleTypeElt) * Fields.size(),
30173019
alignof(TupleType), arena);
30183020
auto New = new (mem) TupleType(Fields, IsCanonical ? &C : nullptr, properties,
3019-
hasInOut);
3021+
hasElementWithOwnership);
30203022
C.getImpl().getArena(arena).TupleTypes.InsertNode(New, InsertPos);
30213023
return New;
30223024
}

lib/SIL/SILFunctionType.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -604,9 +604,10 @@ class DestructureInputs {
604604
ParameterTypeFlags paramFlags, CanType ty) {
605605
CanTupleType tty = dyn_cast<TupleType>(ty);
606606
// If the abstraction pattern is opaque, and the tuple type is
607-
// materializable -- if it doesn't contain an l-value type -- then it's
608-
// a valid target for substitution and we should not expand it.
609-
if (!tty || (pattern.isTypeParameter() && !tty->hasInOutElement())) {
607+
// a valid target for substitution, don't expand it.
608+
if (!tty ||
609+
(pattern.isTypeParameter() &&
610+
!shouldExpandTupleType(tty))) {
610611
visit(paramFlags.getValueOwnership(), /*forSelf=*/false, pattern, ty,
611612
silRepresentation);
612613
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->hasInOutElement())
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: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -872,8 +872,9 @@ namespace {
872872
}
873873
}
874874

875-
// Special-case: tuples containing inouts.
876-
if (inputTupleType && inputTupleType->hasInOutElement()) {
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
@@ -1056,8 +1057,8 @@ namespace {
10561057
CanTupleType inputTupleType,
10571058
AbstractionPattern outputOrigType,
10581059
CanTupleType outputTupleType) {
1059-
assert(!inputTupleType->hasInOutElement() &&
1060-
!outputTupleType->hasInOutElement());
1060+
assert(!inputTupleType->hasElementWithOwnership() &&
1061+
!outputTupleType->hasElementWithOwnership());
10611062
assert(inputTupleType->getNumElements() ==
10621063
outputTupleType->getNumElements());
10631064

@@ -1149,8 +1150,8 @@ namespace {
11491150
// when witness method thunks re-abstract a non-mutating
11501151
// witness for a mutating requirement. The inout self is just
11511152
// loaded to produce a value in this case.
1152-
assert(inputSubstType->hasInOutElement() ||
1153-
!outputSubstType->hasInOutElement());
1153+
assert(inputSubstType->hasElementWithOwnership() ||
1154+
!outputSubstType->hasElementWithOwnership());
11541155
assert(inputSubstType->getNumElements() ==
11551156
outputSubstType->getNumElements());
11561157

@@ -1171,8 +1172,8 @@ namespace {
11711172
ManagedValue inputTupleAddr) {
11721173
assert(inputOrigType.isTypeParameter());
11731174
assert(outputOrigType.matchesTuple(outputSubstType));
1174-
assert(!inputSubstType->hasInOutElement() &&
1175-
!outputSubstType->hasInOutElement());
1175+
assert(!inputSubstType->hasElementWithOwnership() &&
1176+
!outputSubstType->hasElementWithOwnership());
11761177
assert(inputSubstType->getNumElements() ==
11771178
outputSubstType->getNumElements());
11781179

@@ -1218,8 +1219,8 @@ namespace {
12181219
TemporaryInitialization &tupleInit) {
12191220
assert(inputOrigType.matchesTuple(inputSubstType));
12201221
assert(outputOrigType.matchesTuple(outputSubstType));
1221-
assert(!inputSubstType->hasInOutElement() &&
1222-
!outputSubstType->hasInOutElement());
1222+
assert(!inputSubstType->hasElementWithOwnership() &&
1223+
!outputSubstType->hasElementWithOwnership());
12231224
assert(inputSubstType->getNumElements() ==
12241225
outputSubstType->getNumElements());
12251226

test/SILGen/reabstract.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,13 @@ closureTakingOptional({ (_: Any) -> () in })
103103
// CHECK: [[OPTADDR:%.*]] = init_existential_addr [[ANYADDR]] : $*Any, $Optional<Int>
104104
// CHECK: store %0 to [trivial] [[OPTADDR]] : $*Optional<Int>
105105
// CHECK: apply %1([[ANYADDR]]) : $@noescape @callee_guaranteed (@in_guaranteed Any) -> ()
106+
107+
// Same behavior as above with other ownership qualifiers.
108+
func evenLessFun(_ s: __shared C, _ o: __owned C) {}
109+
110+
// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] @$S10reabstract1CCACIeggx_A2CytIegnir_TR : $@convention(thin) (@in_guaranteed C, @in C, @guaranteed @callee_guaranteed (@guaranteed C, @owned C) -> ()) -> @out ()
111+
// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] @$S10reabstract1CCACytIegnir_A2CIeggx_TR : $@convention(thin) (@guaranteed C, @owned C, @guaranteed @callee_guaranteed (@in_guaranteed C, @in C) -> @out ()) -> ()
112+
func testSharedOwnedOpaque(_ s: C, o: C) {
113+
let box = Box(t: evenLessFun)
114+
box.t(s, o)
115+
}
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)