Skip to content

Commit 992396b

Browse files
authored
Merge pull request swiftlang#72431 from meg-gupta/lifetimeinsil
Parse explicit lifetime dependence specifiers in SIL
2 parents 9b41669 + 5ad0a8b commit 992396b

File tree

9 files changed

+225
-15
lines changed

9 files changed

+225
-15
lines changed

include/swift/AST/LifetimeDependence.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace swift {
3030

3131
class AbstractFunctionDecl;
3232
class LifetimeDependentReturnTypeRepr;
33+
class SILParameterInfo;
3334

3435
enum class LifetimeDependenceKind : uint8_t {
3536
Copy = 0,
@@ -138,12 +139,6 @@ class LifetimeDependenceInfo {
138139
unsigned index,
139140
ValueOwnership ownership);
140141

141-
static std::optional<LifetimeDependenceInfo>
142-
fromTypeRepr(AbstractFunctionDecl *afd, Type resultType, bool allowIndex);
143-
144-
static std::optional<LifetimeDependenceInfo> infer(AbstractFunctionDecl *afd,
145-
Type resultType);
146-
147142
public:
148143
LifetimeDependenceInfo()
149144
: inheritLifetimeParamIndices(nullptr),
@@ -193,12 +188,30 @@ class LifetimeDependenceInfo {
193188
std::optional<LifetimeDependenceKind>
194189
getLifetimeDependenceOnParam(unsigned paramIndex);
195190

191+
/// Builds LifetimeDependenceInfo from a swift decl, either from the explicit
192+
/// lifetime dependence specifiers or by inference based on types and
193+
/// ownership modifiers.
196194
static std::optional<LifetimeDependenceInfo>
197195
get(AbstractFunctionDecl *decl, Type resultType, bool allowIndex = false);
198196

197+
/// Builds LifetimeDependenceInfo from the bitvectors passes as parameters.
199198
static LifetimeDependenceInfo
200199
get(ASTContext &ctx, const SmallBitVector &inheritLifetimeIndices,
201200
const SmallBitVector &scopeLifetimeIndices);
201+
202+
/// Builds LifetimeDependenceInfo from a swift decl
203+
static std::optional<LifetimeDependenceInfo>
204+
fromTypeRepr(AbstractFunctionDecl *afd, Type resultType, bool allowIndex);
205+
206+
/// Builds LifetimeDependenceInfo from SIL
207+
static std::optional<LifetimeDependenceInfo>
208+
fromTypeRepr(LifetimeDependentReturnTypeRepr *lifetimeDependentRepr,
209+
SmallVectorImpl<SILParameterInfo> &params, bool hasSelfParam,
210+
DeclContext *dc);
211+
212+
/// Infer LifetimeDependenceInfo
213+
static std::optional<LifetimeDependenceInfo> infer(AbstractFunctionDecl *afd,
214+
Type resultType);
202215
};
203216

204217
} // namespace swift

include/swift/AST/Types.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4155,6 +4155,25 @@ inline bool isGuaranteedParameter(ParameterConvention conv) {
41554155
llvm_unreachable("bad convention kind");
41564156
}
41574157

4158+
inline bool isMutatingParameter(ParameterConvention conv) {
4159+
switch (conv) {
4160+
case ParameterConvention::Indirect_Inout:
4161+
case ParameterConvention::Indirect_InoutAliasable:
4162+
case ParameterConvention::Pack_Inout:
4163+
return true;
4164+
4165+
case ParameterConvention::Direct_Guaranteed:
4166+
case ParameterConvention::Indirect_In_Guaranteed:
4167+
case ParameterConvention::Pack_Guaranteed:
4168+
case ParameterConvention::Indirect_In:
4169+
case ParameterConvention::Direct_Unowned:
4170+
case ParameterConvention::Direct_Owned:
4171+
case ParameterConvention::Pack_Owned:
4172+
return false;
4173+
}
4174+
llvm_unreachable("bad convention kind");
4175+
}
4176+
41584177
/// Returns true if conv indicates a pack parameter.
41594178
inline bool isPackParameter(ParameterConvention conv) {
41604179
switch (conv) {
@@ -4175,6 +4194,8 @@ inline bool isPackParameter(ParameterConvention conv) {
41754194
llvm_unreachable("bad convention kind");
41764195
}
41774196

4197+
StringRef getStringForParameterConvention(ParameterConvention conv);
4198+
41784199
/// A parameter type and the rules for passing it.
41794200
class SILParameterInfo {
41804201
public:

include/swift/Parse/Parser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,7 @@ class Parser {
12121212
return true;
12131213
if (Context.LangOpts.hasFeature(Feature::NonescapableTypes) &&
12141214
(Tok.isContextualKeyword("_resultDependsOn") ||
1215-
Tok.isLifetimeDependenceToken()))
1215+
Tok.isLifetimeDependenceToken(isInSILMode())))
12161216
return true;
12171217
return false;
12181218
}

include/swift/Parse/Token.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,14 @@ class Token {
267267
return MultilineString;
268268
}
269269

270-
bool isLifetimeDependenceToken() const {
270+
bool isLifetimeDependenceToken(bool isInSILMode) const {
271271
return isContextualKeyword("_copy") || isContextualKeyword("_consume") ||
272-
isContextualKeyword("_borrow") || isContextualKeyword("_mutate");
272+
isContextualKeyword("_borrow") || isContextualKeyword("_mutate") ||
273+
// SIL tests are currently written with _inherit/_scope
274+
// Once we have dependsOn()/dependsOn(borrowed:) other tokens go
275+
// away.
276+
(isInSILMode &&
277+
(isContextualKeyword("_inherit") || isContextualKeyword("_scope")));
273278
}
274279

275280
/// Count of extending escaping '#'.

lib/AST/ASTPrinter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7485,7 +7485,7 @@ std::string GenericSignature::getAsString() const {
74857485
return out.str();
74867486
}
74877487

7488-
static StringRef getStringForParameterConvention(ParameterConvention conv) {
7488+
StringRef swift::getStringForParameterConvention(ParameterConvention conv) {
74897489
switch (conv) {
74907490
case ParameterConvention::Indirect_In: return "@in ";
74917491
case ParameterConvention::Indirect_In_Guaranteed: return "@in_guaranteed ";

lib/Parse/ParseDecl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5048,13 +5048,13 @@ ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result,
50485048

50495049
static std::optional<LifetimeDependenceKind>
50505050
getLifetimeDependenceKind(const Token &T) {
5051-
if (T.isContextualKeyword("_copy")) {
5051+
if (T.isContextualKeyword("_copy") || T.isContextualKeyword("_inherit")) {
50525052
return LifetimeDependenceKind::Copy;
50535053
}
50545054
if (T.isContextualKeyword("_consume")) {
50555055
return LifetimeDependenceKind::Consume;
50565056
}
5057-
if (T.isContextualKeyword("_borrow")) {
5057+
if (T.isContextualKeyword("_borrow") || T.isContextualKeyword("_scope")) {
50585058
return LifetimeDependenceKind::Borrow;
50595059
}
50605060
if (T.isContextualKeyword("_mutate")) {
@@ -5454,7 +5454,7 @@ ParserStatus Parser::ParsedTypeAttributeList::slowParse(Parser &P) {
54545454
continue;
54555455
}
54565456

5457-
if (Tok.isLifetimeDependenceToken()) {
5457+
if (Tok.isLifetimeDependenceToken(P.isInSILMode())) {
54585458
if (!P.Context.LangOpts.hasFeature(Feature::NonescapableTypes)) {
54595459
P.diagnose(Tok, diag::requires_experimental_feature,
54605460
"lifetime dependence specifier", false,

lib/Sema/LifetimeDependence.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,81 @@ LifetimeDependenceInfo::fromTypeRepr(AbstractFunctionDecl *afd, Type resultType,
284284
/*isExplicit*/ true);
285285
}
286286

287+
// This utility is similar to its overloaded version that builds the
288+
// LifetimeDependenceInfo from the swift decl. Reason for duplicated code is the
289+
// apis on type and ownership is different in SIL compared to Sema.
290+
std::optional<LifetimeDependenceInfo> LifetimeDependenceInfo::fromTypeRepr(
291+
LifetimeDependentReturnTypeRepr *lifetimeDependentRepr,
292+
SmallVectorImpl<SILParameterInfo> &params, bool hasSelfParam,
293+
DeclContext *dc) {
294+
auto &ctx = dc->getASTContext();
295+
auto &diags = ctx.Diags;
296+
auto capacity = hasSelfParam ? params.size() : params.size() + 1;
297+
298+
SmallBitVector inheritLifetimeParamIndices(capacity);
299+
SmallBitVector scopeLifetimeParamIndices(capacity);
300+
301+
auto updateLifetimeDependenceInfo = [&](LifetimeDependenceSpecifier specifier,
302+
unsigned paramIndexToSet,
303+
ParameterConvention paramConvention) {
304+
auto loc = specifier.getLoc();
305+
auto kind = specifier.getLifetimeDependenceKind();
306+
307+
// Once we have dependsOn()/dependsOn(scoped:) syntax, this enum values will
308+
// be Scope and Inherit.
309+
if (kind == LifetimeDependenceKind::Borrow &&
310+
(!isGuaranteedParameter(paramConvention) &&
311+
!isMutatingParameter(paramConvention))) {
312+
diags.diagnose(loc, diag::lifetime_dependence_cannot_use_kind, "_scope",
313+
getStringForParameterConvention(paramConvention));
314+
return true;
315+
}
316+
317+
if (inheritLifetimeParamIndices.test(paramIndexToSet) ||
318+
scopeLifetimeParamIndices.test(paramIndexToSet)) {
319+
diags.diagnose(loc, diag::lifetime_dependence_duplicate_param_id);
320+
return true;
321+
}
322+
if (kind == LifetimeDependenceKind::Copy) {
323+
inheritLifetimeParamIndices.set(paramIndexToSet);
324+
} else {
325+
assert(kind == LifetimeDependenceKind::Borrow);
326+
scopeLifetimeParamIndices.set(paramIndexToSet);
327+
}
328+
return false;
329+
};
330+
331+
for (auto specifier : lifetimeDependentRepr->getLifetimeDependencies()) {
332+
assert(specifier.getSpecifierKind() ==
333+
LifetimeDependenceSpecifier::SpecifierKind::Ordered);
334+
auto index = specifier.getIndex();
335+
if (index > params.size()) {
336+
diags.diagnose(specifier.getLoc(),
337+
diag::lifetime_dependence_invalid_param_index, index);
338+
return std::nullopt;
339+
}
340+
if (index == 0 && !hasSelfParam) {
341+
diags.diagnose(specifier.getLoc(),
342+
diag::lifetime_dependence_invalid_self);
343+
return std::nullopt;
344+
}
345+
auto param = index == 0 ? params.back() : params[index - 1];
346+
auto paramConvention = param.getConvention();
347+
if (updateLifetimeDependenceInfo(specifier, index, paramConvention)) {
348+
return std::nullopt;
349+
}
350+
}
351+
352+
return LifetimeDependenceInfo(
353+
inheritLifetimeParamIndices.any()
354+
? IndexSubset::get(ctx, inheritLifetimeParamIndices)
355+
: nullptr,
356+
scopeLifetimeParamIndices.any()
357+
? IndexSubset::get(ctx, scopeLifetimeParamIndices)
358+
: nullptr,
359+
/*isExplicit*/ true);
360+
}
361+
287362
std::optional<LifetimeDependenceInfo>
288363
LifetimeDependenceInfo::infer(AbstractFunctionDecl *afd, Type resultType) {
289364
auto *dc = afd->getDeclContext();

lib/Sema/TypeCheckType.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4026,7 +4026,6 @@ NeverNullType TypeResolver::resolveASTFunctionType(
40264026
// TODO: maybe make this the place that claims @escaping.
40274027
bool noescape = isDefaultNoEscapeContext(parentOptions);
40284028

4029-
// TODO: Handle LifetimeDependenceInfo here.
40304029
FunctionType::ExtInfoBuilder extInfoBuilder(
40314030
FunctionTypeRepresentation::Swift, noescape, repr->isThrowing(), thrownTy,
40324031
diffKind, /*clangFunctionType*/ nullptr, isolation,
@@ -4237,7 +4236,6 @@ NeverNullType TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr,
42374236
}
42384237
}
42394238

4240-
// TODO: Handle LifetimeDependenceInfo here.
42414239
auto extInfoBuilder = SILFunctionType::ExtInfoBuilder(
42424240
representation, pseudogeneric, noescape, sendable, async, unimplementable,
42434241
isolation, diffKind, clangFnType, LifetimeDependenceInfo());
@@ -4410,6 +4408,19 @@ NeverNullType TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr,
44104408
extInfoBuilder = extInfoBuilder.withClangFunctionType(clangFnType);
44114409
}
44124410

4411+
std::optional<LifetimeDependenceInfo> lifetimeDependenceInfo;
4412+
if (auto *lifetimeDependentTypeRepr =
4413+
dyn_cast<LifetimeDependentReturnTypeRepr>(
4414+
repr->getResultTypeRepr())) {
4415+
lifetimeDependenceInfo = LifetimeDependenceInfo::fromTypeRepr(
4416+
lifetimeDependentTypeRepr, params, extInfoBuilder.hasSelfParam(),
4417+
getDeclContext());
4418+
if (lifetimeDependenceInfo.has_value()) {
4419+
extInfoBuilder =
4420+
extInfoBuilder.withLifetimeDependenceInfo(*lifetimeDependenceInfo);
4421+
}
4422+
}
4423+
44134424
return SILFunctionType::get(genericSig.getCanonicalSignature(),
44144425
extInfoBuilder.build(), coroutineKind,
44154426
callee, params, yields, results, errorResult,
@@ -4523,6 +4534,14 @@ bool TypeResolver::resolveSingleSILResult(
45234534

45244535
options.setContext(TypeResolverContext::FunctionResult);
45254536

4537+
// Look through LifetimeDependentReturnTypeRepr.
4538+
// LifetimeDependentReturnTypeRepr will be processed separately when building
4539+
// SILFunctionType.
4540+
if (auto *lifetimeDependentTypeRepr =
4541+
dyn_cast<LifetimeDependentReturnTypeRepr>(repr)) {
4542+
repr = lifetimeDependentTypeRepr->getBase();
4543+
}
4544+
45264545
if (auto attrRepr = dyn_cast<AttributedTypeRepr>(repr)) {
45274546
TypeAttrSet attrs(getASTContext());
45284547
auto repr = attrs.accumulate(attrRepr);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// RUN: %target-sil-opt %s \
2+
// RUN: -enable-experimental-feature NonescapableTypes \
3+
// RUN: -enable-experimental-feature NoncopyableGenerics | %FileCheck %s
4+
5+
sil_stage canonical
6+
7+
import Builtin
8+
import Swift
9+
10+
struct BufferView : ~Escapable {
11+
@_hasStorage let ptr: UnsafeRawBufferPointer { get }
12+
@_unsafeNonescapableResult @inlinable init(_ ptr: UnsafeRawBufferPointer)
13+
init(_ ptr: UnsafeRawBufferPointer, _ a: borrowing Array<Int>) -> _borrow(a) Self
14+
}
15+
16+
func derive(_ x: borrowing BufferView) -> _borrow(x) BufferView
17+
18+
func consumeAndCreate(_ x: consuming BufferView) -> _copy(x) BufferView
19+
20+
sil hidden [unsafe_nonescapable_result] @bufferviewinit1 : $@convention(method) (UnsafeRawBufferPointer, @thin BufferView.Type) -> @owned BufferView {
21+
bb0(%0 : $UnsafeRawBufferPointer, %1 : $@thin BufferView.Type):
22+
%2 = alloc_stack [var_decl] $BufferView, var, name "self", implicit
23+
debug_value %0 : $UnsafeRawBufferPointer, let, name "ptr", argno 1
24+
%4 = begin_access [modify] [static] %2 : $*BufferView
25+
%5 = struct_element_addr %4 : $*BufferView, #BufferView.ptr
26+
store %0 to %5 : $*UnsafeRawBufferPointer
27+
end_access %4 : $*BufferView
28+
%8 = struct $BufferView (%0 : $UnsafeRawBufferPointer)
29+
destroy_addr %2 : $*BufferView
30+
dealloc_stack %2 : $*BufferView
31+
return %8 : $BufferView
32+
}
33+
34+
// CHECK-LABEL: sil hidden @bufferviewtest2 : $@convention(method) (UnsafeRawBufferPointer, @guaranteed Array<Int>, @thin BufferView.Type) -> _scope(2) @owned BufferView {
35+
sil hidden @bufferviewtest2 : $@convention(method) (UnsafeRawBufferPointer, @guaranteed Array<Int>, @thin BufferView.Type) -> _scope(2) @owned BufferView {
36+
bb0(%0 : $UnsafeRawBufferPointer, %1 : @noImplicitCopy $Array<Int>, %2 : $@thin BufferView.Type):
37+
%3 = alloc_stack [var_decl] $BufferView, var, name "self", implicit
38+
%6 = begin_access [modify] [static] %3 : $*BufferView
39+
%7 = struct_element_addr %6 : $*BufferView, #BufferView.ptr
40+
store %0 to %7 : $*UnsafeRawBufferPointer
41+
end_access %6 : $*BufferView
42+
%10 = struct $BufferView (%0 : $UnsafeRawBufferPointer)
43+
destroy_addr %3 : $*BufferView
44+
dealloc_stack %3 : $*BufferView
45+
return %10 : $BufferView
46+
}
47+
48+
// CHECK-LABEL: sil hidden @derive : $@convention(thin) (@guaranteed BufferView) -> _scope(1) @owned BufferView {
49+
sil hidden @derive : $@convention(thin) (@guaranteed BufferView) -> _scope(1) @owned BufferView {
50+
bb0(%0 : @noImplicitCopy $BufferView):
51+
%2 = metatype $@thin BufferView.Type
52+
%3 = struct_extract %0 : $BufferView, #BufferView.ptr
53+
%4 = function_ref @bufferviewinit1 : $@convention(method) (UnsafeRawBufferPointer, @thin BufferView.Type) -> @owned BufferView
54+
%5 = apply %4(%3, %2) : $@convention(method) (UnsafeRawBufferPointer, @thin BufferView.Type) -> @owned BufferView
55+
return %5 : $BufferView
56+
}
57+
58+
// CHECK-LABEL: sil hidden @consumeAndCreate : $@convention(thin) (@owned BufferView) -> _inherit(1) @owned BufferView {
59+
sil hidden @consumeAndCreate : $@convention(thin) (@owned BufferView) -> _inherit(1) @owned BufferView {
60+
bb0(%0 : @noImplicitCopy @_eagerMove $BufferView):
61+
%1 = alloc_stack [var_decl] [moveable_value_debuginfo] $BufferView, var, name "x"
62+
%2 = struct_extract %0 : $BufferView, #BufferView.ptr
63+
%3 = struct_extract %2 : $UnsafeRawBufferPointer, #UnsafeRawBufferPointer._position
64+
%4 = struct_extract %0 : $BufferView, #BufferView.ptr
65+
%5 = struct_extract %4 : $UnsafeRawBufferPointer, #UnsafeRawBufferPointer._end
66+
store %0 to %1 : $*BufferView
67+
%7 = metatype $@thin BufferView.Type
68+
%8 = begin_access [read] [static] %1 : $*BufferView
69+
%9 = struct $UnsafeRawBufferPointer (%3 : $Optional<UnsafeRawPointer>, %5 : $Optional<UnsafeRawPointer>)
70+
end_access %8 : $*BufferView
71+
%11 = function_ref @bufferviewinit1 : $@convention(method) (UnsafeRawBufferPointer, @thin BufferView.Type) -> @owned BufferView
72+
%12 = apply %11(%9, %7) : $@convention(method) (UnsafeRawBufferPointer, @thin BufferView.Type) -> @owned BufferView
73+
destroy_addr %1 : $*BufferView
74+
dealloc_stack %1 : $*BufferView
75+
return %12 : $BufferView
76+
}
77+

0 commit comments

Comments
 (0)