Skip to content

Commit c1e77bf

Browse files
committed
[sil-opaque-values] Fix ownership of enum values.
This involved a bit of cleanup in both ownership classification and verification. Trivial argument types now have trivial ownership. Also, the verifier allows - enums with non-trivial ownership to be passed to trivial arguments as long as it can verify that all payloads are trivial. - enums with trivial ownership to be passed as owned arguments. Preventing this didn't make sense.
1 parent 18004f8 commit c1e77bf

File tree

3 files changed

+143
-36
lines changed

3 files changed

+143
-36
lines changed

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ class OwnershipCompatibilityUseChecker
371371
OwnershipUseCheckerResult visitTransformingTerminatorInst(TermInst *TI);
372372

373373
OwnershipUseCheckerResult
374-
visitNonTrivialEnum(EnumDecl *E, ValueOwnershipKind RequiredConvention);
374+
visitEnumArgument(EnumDecl *E, ValueOwnershipKind RequiredConvention);
375375
OwnershipUseCheckerResult
376376
visitApplyParameter(ValueOwnershipKind RequiredConvention,
377377
UseLifetimeConstraint Requirement);
@@ -790,7 +790,7 @@ OwnershipCompatibilityUseChecker::checkTerminatorArgumentMatchesDestBB(
790790
return {matches, lifetimeConstraint};
791791
}
792792

793-
return visitNonTrivialEnum(E, DestBlockArgOwnershipKind);
793+
return visitEnumArgument(E, DestBlockArgOwnershipKind);
794794
}
795795

796796
OwnershipUseCheckerResult
@@ -923,7 +923,7 @@ OwnershipCompatibilityUseChecker::visitReturnInst(ReturnInst *RI) {
923923
}
924924

925925
if (auto *E = getType().getEnumOrBoundGenericEnum()) {
926-
return visitNonTrivialEnum(E, Base);
926+
return visitEnumArgument(E, Base);
927927
}
928928

929929
return {compatibleWithOwnership(Base),
@@ -1011,42 +1011,31 @@ OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitCallee(
10111011
llvm_unreachable("Unhandled ParameterConvention in switch.");
10121012
}
10131013

1014-
OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitNonTrivialEnum(
1014+
// Visit an enum value that is passed at argument position, including block
1015+
// arguments, apply arguments, and return values.
1016+
//
1017+
// The operand definition's ownership kind may be known to be "trivial",
1018+
// but it is still valid to pass that enum to a argument nontrivial type.
1019+
// For example:
1020+
//
1021+
// %val = enum $Optional<SomeClass>, #Optional.none // trivial ownership
1022+
// apply %f(%val) : (@owned Optional<SomeClass>) // owned argument
1023+
OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitEnumArgument(
10151024
EnumDecl *E, ValueOwnershipKind RequiredKind) {
1016-
// Otherwise, first see if the enum is completely trivial. In such a case, we
1017-
// need an argument with a trivial convention. If we have an enum with at
1018-
// least 1 non-trivial case, then we need an argument with a non-trivial
1019-
// convention. If our parameter is trivial, then we just let it through in
1020-
// such a case. Otherwise we need to make sure that the non-trivial ownership
1021-
// convention matches the one on the argument parameter.
1022-
1023-
// Check if this enum has at least one case that is non-trivially typed.
1024-
bool HasNonTrivialCase =
1025-
llvm::any_of(E->getAllElements(), [this](EnumElementDecl *E) -> bool {
1026-
if (!E->hasAssociatedValues())
1027-
return false;
1028-
SILType EnumEltType = getType().getEnumElementType(E, Mod);
1029-
return !EnumEltType.isTrivial(Mod);
1030-
});
1031-
1032-
// If we have all trivial cases, make sure we are compatible with a trivial
1033-
// ownership kind.
1034-
if (!HasNonTrivialCase) {
1035-
return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
1036-
UseLifetimeConstraint::MustBeLive};
1037-
}
1038-
1039-
// Otherwise, if this value is a trivial ownership kind, return.
1025+
// If this value is already categorized as a trivial ownership kind, it is
1026+
// safe to pass to any argument convention.
10401027
if (compatibleWithOwnership(ValueOwnershipKind::Trivial)) {
10411028
return {true, UseLifetimeConstraint::MustBeLive};
10421029
}
10431030

1044-
// And finally finish by making sure that if we have a non-trivial ownership
1045-
// kind that it matches the argument's convention.
1046-
auto lifetimeConstraint = hasExactOwnership(ValueOwnershipKind::Owned)
1031+
// The operand has a non-trivial ownership kind. It must match the argument
1032+
// convention.
1033+
auto ownership = getOwnershipKind();
1034+
auto lifetimeConstraint = (ownership == ValueOwnershipKind::Owned)
10471035
? UseLifetimeConstraint::MustBeInvalidated
10481036
: UseLifetimeConstraint::MustBeLive;
1049-
return {compatibleWithOwnership(RequiredKind), lifetimeConstraint};
1037+
return {compatibleOwnershipKinds(ownership, RequiredKind),
1038+
lifetimeConstraint};
10501039
}
10511040

10521041
// We allow for trivial cases of enums with non-trivial cases to be passed in
@@ -1060,7 +1049,7 @@ OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitApplyParameter(
10601049
if (!E) {
10611050
return {compatibleWithOwnership(Kind), Requirement};
10621051
}
1063-
return visitNonTrivialEnum(E, Kind);
1052+
return visitEnumArgument(E, Kind);
10641053
}
10651054

10661055
// Handle Apply and TryApply.

lib/SIL/SILValue.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ SILModule *ValueBase::getModule() const {
9191
ValueOwnershipKind::ValueOwnershipKind(SILModule &M, SILType Type,
9292
SILArgumentConvention Convention)
9393
: Value() {
94+
// Trivial types can be passed using a variety of conventions. They always
95+
// have trivial ownership.
96+
if (Type.isTrivial(M)) {
97+
Value = ValueOwnershipKind::Trivial;
98+
return;
99+
}
100+
94101
switch (Convention) {
95102
case SILArgumentConvention::Indirect_In:
96103
case SILArgumentConvention::Indirect_In_Constant:
@@ -112,8 +119,7 @@ ValueOwnershipKind::ValueOwnershipKind(SILModule &M, SILType Type,
112119
Value = ValueOwnershipKind::Owned;
113120
return;
114121
case SILArgumentConvention::Direct_Unowned:
115-
Value = Type.isTrivial(M) ? ValueOwnershipKind::Trivial
116-
: ValueOwnershipKind::Unowned;
122+
Value = ValueOwnershipKind::Unowned;
117123
return;
118124
case SILArgumentConvention::Direct_Guaranteed:
119125
Value = ValueOwnershipKind::Guaranteed;

test/SILGen/opaque_ownership.swift

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
public typealias AnyObject = Builtin.AnyObject
55

6-
precedencegroup CastingPrecedence {}
76
precedencegroup AssignmentPrecedence {}
7+
precedencegroup CastingPrecedence {}
8+
precedencegroup ComparisonPrecedence {}
89

910
public protocol _ObjectiveCBridgeable {}
1011

@@ -58,6 +59,117 @@ public func unsafeBitCast<T, U>(_ x: T, to type: U.Type) -> U {
5859
return Builtin.reinterpretCast(x)
5960
}
6061

62+
// A lot of standard library support is necessary to support raw enums.
63+
// --------------------------------------------------------------------
64+
65+
infix operator == : ComparisonPrecedence
66+
infix operator ~= : ComparisonPrecedence
67+
68+
public struct Bool {
69+
var _value: Builtin.Int1
70+
71+
public init() {
72+
let zero: Int64 = 0
73+
self._value = Builtin.trunc_Int64_Int1(zero._value)
74+
}
75+
76+
internal init(_ v: Builtin.Int1) { self._value = v }
77+
78+
public init(_ value: Bool) {
79+
self = value
80+
}
81+
}
82+
83+
extension Bool {
84+
public func _getBuiltinLogicValue() -> Builtin.Int1 {
85+
return _value
86+
}
87+
}
88+
89+
public protocol Equatable {
90+
/// Returns a Boolean value indicating whether two values are equal.
91+
///
92+
/// Equality is the inverse of inequality. For any values `a` and `b`,
93+
/// `a == b` implies that `a != b` is `false`.
94+
///
95+
/// - Parameters:
96+
/// - lhs: A value to compare.
97+
/// - rhs: Another value to compare.
98+
static func == (lhs: Self, rhs: Self) -> Bool
99+
}
100+
101+
public func ~= <T : Equatable>(a: T, b: T) -> Bool {
102+
return a == b
103+
}
104+
105+
public protocol RawRepresentable {
106+
associatedtype RawValue
107+
108+
init?(rawValue: RawValue)
109+
110+
var rawValue: RawValue { get }
111+
}
112+
113+
public func == <T : RawRepresentable>(lhs: T, rhs: T) -> Bool
114+
where T.RawValue : Equatable {
115+
return lhs.rawValue == rhs.rawValue
116+
}
117+
118+
public typealias _MaxBuiltinIntegerType = Builtin.Int2048
119+
120+
public protocol _ExpressibleByBuiltinIntegerLiteral {
121+
init(_builtinIntegerLiteral value: _MaxBuiltinIntegerType)
122+
}
123+
124+
public protocol ExpressibleByIntegerLiteral {
125+
associatedtype IntegerLiteralType : _ExpressibleByBuiltinIntegerLiteral
126+
127+
init(integerLiteral value: IntegerLiteralType)
128+
}
129+
130+
extension ExpressibleByIntegerLiteral
131+
where Self : _ExpressibleByBuiltinIntegerLiteral {
132+
@_transparent
133+
public init(integerLiteral value: Self) {
134+
self = value
135+
}
136+
}
137+
138+
public protocol ExpressibleByStringLiteral {}
139+
public protocol ExpressibleByFloatLiteral {}
140+
public protocol ExpressibleByUnicodeScalarLiteral {}
141+
public protocol ExpressibleByExtendedGraphemeClusterLiteral {}
142+
143+
public struct Int64 : ExpressibleByIntegerLiteral, _ExpressibleByBuiltinIntegerLiteral, Equatable {
144+
public var _value: Builtin.Int64
145+
public init(_builtinIntegerLiteral x: _MaxBuiltinIntegerType) {
146+
_value = Builtin.s_to_s_checked_trunc_Int2048_Int64(x).0
147+
}
148+
public typealias IntegerLiteralType = Int64
149+
public init(integerLiteral value: Int64) {
150+
self = value
151+
}
152+
public static func ==(_ lhs: Int64, rhs: Int64) -> Bool {
153+
return Bool(Builtin.cmp_eq_Int64(lhs._value, rhs._value))
154+
}
155+
}
156+
157+
// Test ownership of multi-case Enum values in the context of @trivial to @in thunks.
158+
// ---
159+
// CHECK-LABEL: sil private [transparent] [thunk] @_T0s17FloatingPointSignOs9EquatablessACP2eeoiSbx_xtFZTW : $@convention(witness_method) (@in FloatingPointSign, @in FloatingPointSign, @thick FloatingPointSign.Type) -> Bool {
160+
// CHECK: bb0(%0 : @trivial $FloatingPointSign, %1 : @trivial $FloatingPointSign, %2 : @trivial $@thick FloatingPointSign.Type):
161+
// CHECK: %3 = function_ref @_T0s2eeoiSbx_xts16RawRepresentableRzs9Equatable0B5ValueRpzlF : $@convention(thin) <τ_0_0 where τ_0_0 : RawRepresentable, τ_0_0.RawValue : Equatable> (@in τ_0_0, @in τ_0_0) -> Bool
162+
// CHECK: %4 = apply %3<FloatingPointSign, Int64>(%0, %1) : $@convention(thin) <τ_0_0 where τ_0_0 : RawRepresentable, τ_0_0.RawValue : Equatable> (@in τ_0_0, @in τ_0_0) -> Bool
163+
// CHECK: return %4 : $Bool
164+
// CHECK-LABEL: } // end sil function '_T0s17FloatingPointSignOs9EquatablessACP2eeoiSbx_xtFZTW'
165+
public enum FloatingPointSign: Int64 {
166+
/// The sign for a positive value.
167+
case plus
168+
169+
/// The sign for a negative value.
170+
case minus
171+
}
172+
61173
#if os(OSX)
62174
// Test open_existential_value used in a conversion context.
63175
// (the actual bridging call is dropped because we don't import Swift).

0 commit comments

Comments
 (0)