Skip to content

Commit 86bdca5

Browse files
authored
Merge pull request #82354 from meg-gupta/lifetimeenum
Add lifetime dependencies on function types representing ~Escapable enum elements with ~Escapable payloads
2 parents d0b34c2 + 0eb32f6 commit 86bdca5

File tree

12 files changed

+272
-36
lines changed

12 files changed

+272
-36
lines changed

include/swift/AST/Decl.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8779,7 +8779,8 @@ class EnumCaseDecl final : public Decl,
87798779
/// EnumCaseDecl.
87808780
class EnumElementDecl : public DeclContext, public ValueDecl {
87818781
friend class EnumRawValuesRequest;
8782-
8782+
friend class LifetimeDependenceInfoRequest;
8783+
87838784
/// This is the type specified with the enum element, for
87848785
/// example 'Int' in 'case Y(Int)'. This is null if there is no type
87858786
/// associated with this element, as in 'case Z' or in all elements of enum
@@ -8791,6 +8792,11 @@ class EnumElementDecl : public DeclContext, public ValueDecl {
87918792
/// The raw value literal for the enum element, or null.
87928793
LiteralExpr *RawValueExpr;
87938794

8795+
protected:
8796+
struct {
8797+
unsigned NoLifetimeDependenceInfo : 1;
8798+
} LazySemanticInfo = {};
8799+
87948800
public:
87958801
EnumElementDecl(SourceLoc IdentifierLoc, DeclName Name,
87968802
ParameterList *Params,

include/swift/AST/LifetimeDependence.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,7 @@ class LifetimeDependenceInfo {
324324
/// Builds LifetimeDependenceInfo from a swift decl, either from the explicit
325325
/// lifetime dependence specifiers or by inference based on types and
326326
/// ownership modifiers.
327-
static std::optional<ArrayRef<LifetimeDependenceInfo>>
328-
get(AbstractFunctionDecl *decl);
327+
static std::optional<ArrayRef<LifetimeDependenceInfo>> get(ValueDecl *decl);
329328

330329
/// Builds LifetimeDependenceInfo from SIL
331330
static std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>

include/swift/AST/TypeCheckRequests.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5114,8 +5114,7 @@ class ImportDeclRequest
51145114
class LifetimeDependenceInfoRequest
51155115
: public SimpleRequest<
51165116
LifetimeDependenceInfoRequest,
5117-
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>(
5118-
AbstractFunctionDecl *),
5117+
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>(ValueDecl *),
51195118
RequestFlags::SeparatelyCached | RequestFlags::SplitCached> {
51205119
public:
51215120
using SimpleRequest::SimpleRequest;
@@ -5124,7 +5123,7 @@ class LifetimeDependenceInfoRequest
51245123
friend SimpleRequest;
51255124

51265125
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
5127-
evaluate(Evaluator &evaluator, AbstractFunctionDecl *AFD) const;
5126+
evaluate(Evaluator &evaluator, ValueDecl *AFD) const;
51285127

51295128
public:
51305129
// Separate caching.

lib/AST/LifetimeDependence.cpp

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ void LifetimeDependenceInfo::getConcatenatedData(
252252
}
253253

254254
class LifetimeDependenceChecker {
255-
AbstractFunctionDecl *afd;
255+
ValueDecl *decl;
256256

257257
DeclContext *dc;
258258
ASTContext &ctx;
@@ -273,9 +273,8 @@ class LifetimeDependenceChecker {
273273
bool performedDiagnostics = false;
274274

275275
public:
276-
LifetimeDependenceChecker(AbstractFunctionDecl *afd):
277-
afd(afd), dc(afd->getDeclContext()), ctx(dc->getASTContext())
278-
{
276+
LifetimeDependenceChecker(AbstractFunctionDecl *afd)
277+
: decl(afd), dc(afd->getDeclContext()), ctx(dc->getASTContext()) {
279278
auto resultTypeRepr = afd->getResultTypeRepr();
280279
returnLoc = resultTypeRepr ? resultTypeRepr->getLoc() : afd->getLoc();
281280

@@ -287,18 +286,25 @@ class LifetimeDependenceChecker {
287286
}
288287
}
289288

289+
LifetimeDependenceChecker(EnumElementDecl *eed)
290+
: decl(eed), dc(eed->getDeclContext()), ctx(dc->getASTContext()) {
291+
auto *paramList = eed->getParameterList();
292+
resultIndex = paramList ? eed->getParameterList()->size() + 1 : 1;
293+
}
294+
290295
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
291296
currentDependencies() const {
292297
if (lifetimeDependencies.empty()) {
293298
return std::nullopt;
294299
}
295-
return afd->getASTContext().AllocateCopy(lifetimeDependencies);
300+
return decl->getASTContext().AllocateCopy(lifetimeDependencies);
296301
}
297302

298303
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>> checkFuncDecl() {
299-
assert(isa<FuncDecl>(afd) || isa<ConstructorDecl>(afd));
304+
assert(isa<FuncDecl>(decl) || isa<ConstructorDecl>(decl));
300305
assert(lifetimeDependencies.empty());
301306

307+
auto *afd = cast<AbstractFunctionDecl>(decl);
302308
// Handle Builtins first because, even though Builtins require
303309
// LifetimeDependence, we don't force the experimental feature
304310
// to be enabled when importing the Builtin module.
@@ -351,6 +357,52 @@ class LifetimeDependenceChecker {
351357
return currentDependencies();
352358
}
353359

360+
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>> checkEnumElementDecl() {
361+
auto *eed = cast<EnumElementDecl>(decl);
362+
auto *parentEnum = eed->getParentEnum();
363+
auto enumType =
364+
parentEnum->mapTypeIntoContext(parentEnum->getDeclaredInterfaceType());
365+
366+
// Add early bailout for imported enums.
367+
if (parentEnum->hasClangNode()) {
368+
return std::nullopt;
369+
}
370+
371+
// Escapable enum, bailout.
372+
if (!isDiagnosedNonEscapable(enumType)) {
373+
return std::nullopt;
374+
}
375+
auto *params = eed->getParameterList();
376+
// No payload, bailout.
377+
if (!params) {
378+
return std::nullopt;
379+
}
380+
381+
auto resultIndex = params->size() + /*selfType*/ 1;
382+
auto capacity = resultIndex + 1;
383+
SmallBitVector inheritIndices(capacity);
384+
SmallVector<LifetimeDependenceInfo, 1> lifetimeDependencies;
385+
386+
// Add all indices of ~Escapable parameters as lifetime dependence sources.
387+
for (size_t i = 0; i < params->size(); i++) {
388+
auto paramType = params->get(i)->getTypeInContext();
389+
if (!isDiagnosedNonEscapable(paramType)) {
390+
continue;
391+
}
392+
inheritIndices.set(i);
393+
}
394+
if (inheritIndices.none()) {
395+
return std::nullopt;
396+
}
397+
auto lifetimeDependenceInfo = LifetimeDependenceInfo(
398+
IndexSubset::get(eed->getASTContext(), inheritIndices), nullptr,
399+
resultIndex,
400+
/*isImmortal*/ false);
401+
lifetimeDependencies.push_back(lifetimeDependenceInfo);
402+
403+
return eed->getASTContext().AllocateCopy(lifetimeDependencies);
404+
}
405+
354406
protected:
355407
template<typename ...ArgTypes>
356408
InFlightDiagnostic diagnose(
@@ -367,9 +419,7 @@ class LifetimeDependenceChecker {
367419
return ctx.Diags.diagnose(decl, Diagnostic(id, std::move(args)...));
368420
}
369421

370-
bool isInit() const {
371-
return isa<ConstructorDecl>(afd);
372-
}
422+
bool isInit() const { return isa<ConstructorDecl>(decl); }
373423

374424
// For initializers, the implicit self parameter is ignored and instead shows
375425
// up as the result type.
@@ -381,11 +431,13 @@ class LifetimeDependenceChecker {
381431
// the extra formal self parameter, a dependency targeting the formal result
382432
// index would incorrectly target the SIL metatype parameter.
383433
bool hasImplicitSelfParam() const {
434+
auto *afd = cast<AbstractFunctionDecl>(decl);
384435
return !isInit() && afd->hasImplicitSelfDecl();
385436
}
386437

387438
// In SIL, implicit initializers and accessors become explicit.
388439
bool isImplicitOrSIL() const {
440+
auto *afd = cast<AbstractFunctionDecl>(decl);
389441
if (afd->isImplicit()) {
390442
return true;
391443
}
@@ -404,7 +456,7 @@ class LifetimeDependenceChecker {
404456
bool isInterfaceFile() const {
405457
// TODO: remove this check once all compilers that are rev-locked to the
406458
// stdlib print the 'copy' dependence kind in the interface (Aug '25)
407-
if (auto *sf = afd->getParentSourceFile()) {
459+
if (auto *sf = decl->getDeclContext()->getParentSourceFile()) {
408460
if (sf->Kind == SourceFileKind::Interface) {
409461
return true;
410462
}
@@ -419,6 +471,7 @@ class LifetimeDependenceChecker {
419471
}
420472

421473
std::string diagnosticQualifier() const {
474+
auto *afd = cast<AbstractFunctionDecl>(decl);
422475
if (afd->isImplicit()) {
423476
if (isInit()) {
424477
return "an implicit initializer";
@@ -463,6 +516,7 @@ class LifetimeDependenceChecker {
463516
// initializers, the inout self parameter is actually considered the result
464517
// type so is not handled here.
465518
void diagnoseMissingSelfDependencies(DiagID diagID) {
519+
auto *afd = cast<AbstractFunctionDecl>(decl);
466520
if (!hasImplicitSelfParam()) {
467521
return;
468522
}
@@ -483,6 +537,7 @@ class LifetimeDependenceChecker {
483537
}
484538

485539
void diagnoseMissingInoutDependencies(DiagID diagID) {
540+
auto *afd = cast<AbstractFunctionDecl>(decl);
486541
unsigned paramIndex = 0;
487542
for (auto *param : *afd->getParameters()) {
488543
SWIFT_DEFER { paramIndex++; };
@@ -529,6 +584,7 @@ class LifetimeDependenceChecker {
529584

530585
bool isCompatibleWithOwnership(LifetimeDependenceKind kind, Type type,
531586
ValueOwnership ownership) const {
587+
auto *afd = cast<AbstractFunctionDecl>(decl);
532588
if (kind == LifetimeDependenceKind::Inherit) {
533589
return true;
534590
}
@@ -569,6 +625,7 @@ class LifetimeDependenceChecker {
569625
};
570626

571627
TargetDeps createDeps(unsigned targetIndex) {
628+
auto *afd = cast<AbstractFunctionDecl>(decl);
572629
unsigned capacity = afd->hasImplicitSelfDecl()
573630
? (afd->getParameters()->size() + 1)
574631
: afd->getParameters()->size();
@@ -599,6 +656,7 @@ class LifetimeDependenceChecker {
599656
}
600657

601658
Type getResultOrYield() const {
659+
auto *afd = cast<AbstractFunctionDecl>(decl);
602660
if (auto *accessor = dyn_cast<AccessorDecl>(afd)) {
603661
if (accessor->isCoroutine()) {
604662
auto yieldTyInContext = accessor->mapTypeIntoContext(
@@ -618,11 +676,12 @@ class LifetimeDependenceChecker {
618676

619677
std::optional<LifetimeDependenceKind>
620678
getDependenceKindFromDescriptor(LifetimeDescriptor descriptor,
621-
ParamDecl *decl) {
679+
ParamDecl *paramDecl) {
680+
auto *afd = cast<AbstractFunctionDecl>(decl);
622681
auto loc = descriptor.getLoc();
623-
auto type = decl->getTypeInContext();
682+
auto type = paramDecl->getTypeInContext();
624683
auto parsedLifetimeKind = descriptor.getParsedLifetimeDependenceKind();
625-
auto ownership = decl->getValueOwnership();
684+
auto ownership = paramDecl->getValueOwnership();
626685
auto loweredOwnership = ownership != ValueOwnership::Default
627686
? ownership
628687
: getLoweredOwnership(afd);
@@ -704,6 +763,7 @@ class LifetimeDependenceChecker {
704763
// Finds the ParamDecl* and its index from a LifetimeDescriptor
705764
std::optional<std::pair<ParamDecl *, unsigned>>
706765
getParamDeclFromDescriptor(LifetimeDescriptor descriptor) {
766+
auto *afd = cast<AbstractFunctionDecl>(decl);
707767
switch (descriptor.getDescriptorKind()) {
708768
case LifetimeDescriptor::DescriptorKind::Named: {
709769
unsigned paramIndex = 0;
@@ -752,6 +812,7 @@ class LifetimeDependenceChecker {
752812
}
753813

754814
std::optional<ArrayRef<LifetimeDependenceInfo>> checkAttribute() {
815+
auto *afd = cast<AbstractFunctionDecl>(decl);
755816
SmallVector<LifetimeDependenceInfo, 1> lifetimeDependencies;
756817
llvm::SmallSet<unsigned, 1> lifetimeDependentTargets;
757818
auto lifetimeAttrs = afd->getAttrs().getAttributes<LifetimeAttr>();
@@ -776,6 +837,7 @@ class LifetimeDependenceChecker {
776837

777838
std::optional<LifetimeDependenceInfo>
778839
checkAttributeEntry(LifetimeEntry *entry) {
840+
auto *afd = cast<AbstractFunctionDecl>(decl);
779841
auto capacity = afd->hasImplicitSelfDecl()
780842
? (afd->getParameters()->size() + 1)
781843
: afd->getParameters()->size();
@@ -897,6 +959,7 @@ class LifetimeDependenceChecker {
897959
/// If the current function is a mutating method and 'self' is non-Escapable,
898960
/// return 'self's ParamDecl.
899961
bool isMutatingNonEscapableSelf() {
962+
auto *afd = cast<AbstractFunctionDecl>(decl);
900963
if (!hasImplicitSelfParam())
901964
return false;
902965

@@ -914,6 +977,7 @@ class LifetimeDependenceChecker {
914977
// Infer method dependence of result on self for
915978
// methods, getters, and _modify accessors.
916979
void inferNonEscapableResultOnSelf() {
980+
auto *afd = cast<AbstractFunctionDecl>(decl);
917981
Type selfTypeInContext = dc->getSelfTypeInContext();
918982
if (selfTypeInContext->hasError()) {
919983
return;
@@ -980,6 +1044,7 @@ class LifetimeDependenceChecker {
9801044
// stored property (for getters or initializers).
9811045
std::optional<LifetimeDependenceKind>
9821046
inferLifetimeDependenceKind(Type sourceType, ValueOwnership ownership) {
1047+
auto *afd = cast<AbstractFunctionDecl>(decl);
9831048
if (!sourceType->isEscapable()) {
9841049
return LifetimeDependenceKind::Inherit;
9851050
}
@@ -1003,6 +1068,7 @@ class LifetimeDependenceChecker {
10031068
// to an implicit setter, because the implementation is simply an assignment
10041069
// to stored property.
10051070
void inferImplicitInit() {
1071+
auto *afd = cast<AbstractFunctionDecl>(decl);
10061072
if (afd->getParameters()->size() == 0) {
10071073
// Empty ~Escapable types can be implicitly initialized without any
10081074
// dependencies. In SIL, implicit initializers become explicit. Set
@@ -1042,6 +1108,7 @@ class LifetimeDependenceChecker {
10421108
// inference if any exist, infer scoped dependency, or infer no
10431109
// dependency. Implicit setters for Escapable properties are not inferred.
10441110
void inferNonEscapableResultOnParam() {
1111+
auto *afd = cast<AbstractFunctionDecl>(decl);
10451112
// This is only called when there is no 'self' argument that can be the
10461113
// source of a dependence.
10471114
assert(!hasImplicitSelfParam());
@@ -1091,6 +1158,7 @@ class LifetimeDependenceChecker {
10911158
// Lazy inference for .swiftinterface backward compatibility and
10921159
// experimentation. Inference cases can be added but not removed.
10931160
void lazillyInferNonEscapableResultOnParam() {
1161+
auto *afd = cast<AbstractFunctionDecl>(decl);
10941162
std::optional<unsigned> candidateParamIndex;
10951163
std::optional<LifetimeDependenceKind> candidateLifetimeKind;
10961164
unsigned paramIndex = 0;
@@ -1137,6 +1205,7 @@ class LifetimeDependenceChecker {
11371205
// Infer a mutating 'self' dependency when 'self' is non-Escapable and the
11381206
// result is 'void'.
11391207
void inferMutatingSelf() {
1208+
auto *afd = cast<AbstractFunctionDecl>(decl);
11401209
if (!isMutatingNonEscapableSelf()) {
11411210
return;
11421211
}
@@ -1166,7 +1235,7 @@ class LifetimeDependenceChecker {
11661235
// Infer dependence for an accessor whose non-escapable result depends on
11671236
// self. This includes _read and _modify.
11681237
void inferAccessor(AccessorDecl *accessor, Type selfTypeInContext) {
1169-
// Explicit accessors require explicit lifetime dependencies.
1238+
auto *afd = cast<AbstractFunctionDecl>(decl);
11701239
if (!isImplicitOrSIL() && !useLazyInference()) {
11711240
return;
11721241
}
@@ -1205,6 +1274,7 @@ class LifetimeDependenceChecker {
12051274
break;
12061275
case AccessorKind::Set: {
12071276
const unsigned newValIdx = 0;
1277+
auto *afd = cast<AbstractFunctionDecl>(decl);
12081278
auto *param = afd->getParameters()->get(newValIdx);
12091279
Type paramTypeInContext =
12101280
afd->mapTypeIntoContext(param->getInterfaceType());
@@ -1285,6 +1355,7 @@ class LifetimeDependenceChecker {
12851355
}
12861356
}
12871357
}
1358+
auto *afd = cast<AbstractFunctionDecl>(decl);
12881359
// Either a Get or Modify without any wrapped accessor. Handle these like a
12891360
// read of the stored property.
12901361
return inferLifetimeDependenceKind(
@@ -1315,6 +1386,7 @@ class LifetimeDependenceChecker {
13151386
// Do not issue any diagnostics. This inference is triggered even when the
13161387
// feature is disabled!
13171388
void inferInoutParams() {
1389+
auto *afd = cast<AbstractFunctionDecl>(decl);
13181390
if (isMutatingNonEscapableSelf()) {
13191391
return;
13201392
}
@@ -1347,6 +1419,7 @@ class LifetimeDependenceChecker {
13471419
}
13481420

13491421
void inferUnambiguousInoutParams() {
1422+
auto *afd = cast<AbstractFunctionDecl>(decl);
13501423
if (afd->getParameters()->size() != 1) {
13511424
return;
13521425
}
@@ -1364,6 +1437,7 @@ class LifetimeDependenceChecker {
13641437
}
13651438

13661439
void inferBuiltin() {
1440+
auto *afd = cast<AbstractFunctionDecl>(decl);
13671441
// Normal inout parameter inference works for most generic Builtins.
13681442
inferUnambiguousInoutParams();
13691443
if (!lifetimeDependencies.empty()) {
@@ -1397,8 +1471,12 @@ class LifetimeDependenceChecker {
13971471
};
13981472

13991473
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
1400-
LifetimeDependenceInfo::get(AbstractFunctionDecl *afd) {
1401-
return LifetimeDependenceChecker(afd).checkFuncDecl();
1474+
LifetimeDependenceInfo::get(ValueDecl *decl) {
1475+
if (auto *afd = dyn_cast<AbstractFunctionDecl>(decl)) {
1476+
return LifetimeDependenceChecker(afd).checkFuncDecl();
1477+
}
1478+
auto *eed = cast<EnumElementDecl>(decl);
1479+
return LifetimeDependenceChecker(eed).checkEnumElementDecl();
14021480
}
14031481

14041482
// This implements the logic for SIL type descriptors similar to source-level

0 commit comments

Comments
 (0)