Skip to content

Commit 1d1f9b0

Browse files
authored
Merge pull request #82431 from meg-gupta/lifetimeenumcp
[6.2] Add lifetime dependencies on function types representing ~Escapable enum elements with ~Escapable payloads
2 parents 2371e1f + 41881a8 commit 1d1f9b0

File tree

12 files changed

+270
-35
lines changed

12 files changed

+270
-35
lines changed

include/swift/AST/Decl.h

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

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

include/swift/AST/LifetimeDependence.h

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

334333
/// Builds LifetimeDependenceInfo from SIL
335334
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
@@ -5095,8 +5095,7 @@ class ImportDeclRequest
50955095
class LifetimeDependenceInfoRequest
50965096
: public SimpleRequest<
50975097
LifetimeDependenceInfoRequest,
5098-
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>(
5099-
AbstractFunctionDecl *),
5098+
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>(ValueDecl *),
51005099
RequestFlags::SeparatelyCached | RequestFlags::SplitCached> {
51015100
public:
51025101
using SimpleRequest::SimpleRequest;
@@ -5105,7 +5104,7 @@ class LifetimeDependenceInfoRequest
51055104
friend SimpleRequest;
51065105

51075106
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
5108-
evaluate(Evaluator &evaluator, AbstractFunctionDecl *AFD) const;
5107+
evaluate(Evaluator &evaluator, ValueDecl *AFD) const;
51095108

51105109
public:
51115110
// Separate caching.

lib/AST/LifetimeDependence.cpp

Lines changed: 92 additions & 15 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
}
@@ -418,6 +470,7 @@ class LifetimeDependenceChecker {
418470
}
419471

420472
std::string diagnosticQualifier() const {
473+
auto *afd = cast<AbstractFunctionDecl>(decl);
421474
if (afd->isImplicit()) {
422475
if (isInit()) {
423476
return "an implicit initializer";
@@ -462,6 +515,7 @@ class LifetimeDependenceChecker {
462515
// initializers, the inout self parameter is actually considered the result
463516
// type so is not handled here.
464517
void diagnoseMissingSelfDependencies(DiagID diagID) {
518+
auto *afd = cast<AbstractFunctionDecl>(decl);
465519
if (!hasImplicitSelfParam()) {
466520
return;
467521
}
@@ -482,6 +536,7 @@ class LifetimeDependenceChecker {
482536
}
483537

484538
void diagnoseMissingInoutDependencies(DiagID diagID) {
539+
auto *afd = cast<AbstractFunctionDecl>(decl);
485540
unsigned paramIndex = 0;
486541
for (auto *param : *afd->getParameters()) {
487542
SWIFT_DEFER { paramIndex++; };
@@ -528,6 +583,7 @@ class LifetimeDependenceChecker {
528583

529584
bool isCompatibleWithOwnership(LifetimeDependenceKind kind, Type type,
530585
ValueOwnership ownership) const {
586+
auto *afd = cast<AbstractFunctionDecl>(decl);
531587
if (kind == LifetimeDependenceKind::Inherit) {
532588
return true;
533589
}
@@ -568,6 +624,7 @@ class LifetimeDependenceChecker {
568624
};
569625

570626
TargetDeps createDeps(unsigned targetIndex) {
627+
auto *afd = cast<AbstractFunctionDecl>(decl);
571628
unsigned capacity = afd->hasImplicitSelfDecl()
572629
? (afd->getParameters()->size() + 1)
573630
: afd->getParameters()->size();
@@ -598,6 +655,7 @@ class LifetimeDependenceChecker {
598655
}
599656

600657
Type getResultOrYield() const {
658+
auto *afd = cast<AbstractFunctionDecl>(decl);
601659
if (auto *accessor = dyn_cast<AccessorDecl>(afd)) {
602660
if (accessor->isCoroutine()) {
603661
auto yieldTyInContext = accessor->mapTypeIntoContext(
@@ -617,11 +675,12 @@ class LifetimeDependenceChecker {
617675

618676
std::optional<LifetimeDependenceKind>
619677
getDependenceKindFromDescriptor(LifetimeDescriptor descriptor,
620-
ParamDecl *decl) {
678+
ParamDecl *paramDecl) {
679+
auto *afd = cast<AbstractFunctionDecl>(decl);
621680
auto loc = descriptor.getLoc();
622-
auto type = decl->getTypeInContext();
681+
auto type = paramDecl->getTypeInContext();
623682
auto parsedLifetimeKind = descriptor.getParsedLifetimeDependenceKind();
624-
auto ownership = decl->getValueOwnership();
683+
auto ownership = paramDecl->getValueOwnership();
625684
auto loweredOwnership = ownership != ValueOwnership::Default
626685
? ownership
627686
: getLoweredOwnership(afd);
@@ -703,6 +762,7 @@ class LifetimeDependenceChecker {
703762
// Finds the ParamDecl* and its index from a LifetimeDescriptor
704763
std::optional<std::pair<ParamDecl *, unsigned>>
705764
getParamDeclFromDescriptor(LifetimeDescriptor descriptor) {
765+
auto *afd = cast<AbstractFunctionDecl>(decl);
706766
switch (descriptor.getDescriptorKind()) {
707767
case LifetimeDescriptor::DescriptorKind::Named: {
708768
unsigned paramIndex = 0;
@@ -751,6 +811,7 @@ class LifetimeDependenceChecker {
751811
}
752812

753813
std::optional<ArrayRef<LifetimeDependenceInfo>> checkAttribute() {
814+
auto *afd = cast<AbstractFunctionDecl>(decl);
754815
SmallVector<LifetimeDependenceInfo, 1> lifetimeDependencies;
755816
llvm::SmallSet<unsigned, 1> lifetimeDependentTargets;
756817
auto lifetimeAttrs = afd->getAttrs().getAttributes<LifetimeAttr>();
@@ -775,6 +836,7 @@ class LifetimeDependenceChecker {
775836

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

@@ -912,6 +975,7 @@ class LifetimeDependenceChecker {
912975

913976
// Infer method dependence: result depends on self. This includes _modify.
914977
void inferNonEscapableResultOnSelf() {
978+
auto *afd = cast<AbstractFunctionDecl>(decl);
915979
Type selfTypeInContext = dc->getSelfTypeInContext();
916980
if (selfTypeInContext->hasError()) {
917981
return;
@@ -963,6 +1027,7 @@ class LifetimeDependenceChecker {
9631027

9641028
std::optional<LifetimeDependenceKind>
9651029
inferLifetimeDependenceKind(Type sourceType, ValueOwnership ownership) {
1030+
auto *afd = cast<AbstractFunctionDecl>(decl);
9661031
if (!sourceType->isEscapable()) {
9671032
return LifetimeDependenceKind::Inherit;
9681033
}
@@ -985,6 +1050,7 @@ class LifetimeDependenceChecker {
9851050
// to an implicit setter, because the implementation is simply an assignment
9861051
// to stored property.
9871052
void inferImplicitInit() {
1053+
auto *afd = cast<AbstractFunctionDecl>(decl);
9881054
if (afd->getParameters()->size() == 0) {
9891055
// Empty ~Escapable types can be implicitly initialized without any
9901056
// dependencies. In SIL, implicit initializers become explicit. Set
@@ -1024,6 +1090,7 @@ class LifetimeDependenceChecker {
10241090
// inference if any exist, infer scoped dependency, or infer no
10251091
// dependency. Implicit setters for Escapable properties are not inferred.
10261092
void inferNonEscapableResultOnParam() {
1093+
auto *afd = cast<AbstractFunctionDecl>(decl);
10271094
// This is only called when there is no 'self' argument that can be the
10281095
// source of a dependence.
10291096
assert(!hasImplicitSelfParam());
@@ -1073,6 +1140,7 @@ class LifetimeDependenceChecker {
10731140
// Lazy inference for .swiftinterface backward compatibility and
10741141
// experimentation. Inference cases can be added but not removed.
10751142
void lazillyInferNonEscapableResultOnParam() {
1143+
auto *afd = cast<AbstractFunctionDecl>(decl);
10761144
std::optional<unsigned> candidateParamIndex;
10771145
std::optional<LifetimeDependenceKind> candidateLifetimeKind;
10781146
unsigned paramIndex = 0;
@@ -1119,6 +1187,7 @@ class LifetimeDependenceChecker {
11191187
// Infer a mutating 'self' dependency when 'self' is non-Escapable and the
11201188
// result is 'void'.
11211189
void inferMutatingSelf() {
1190+
auto *afd = cast<AbstractFunctionDecl>(decl);
11221191
if (!isMutatingNonEscapableSelf()) {
11231192
return;
11241193
}
@@ -1144,6 +1213,7 @@ class LifetimeDependenceChecker {
11441213

11451214
// Infer a mutating accessor's non-Escapable 'self' dependencies.
11461215
void inferMutatingAccessor(AccessorDecl *accessor) {
1216+
auto *afd = cast<AbstractFunctionDecl>(decl);
11471217
if (!isImplicitOrSIL() && !useLazyInference()) {
11481218
// Explicit setters require explicit lifetime dependencies.
11491219
return;
@@ -1231,6 +1301,7 @@ class LifetimeDependenceChecker {
12311301
// Do not issue any diagnostics. This inference is triggered even when the
12321302
// feature is disabled!
12331303
void inferInoutParams() {
1304+
auto *afd = cast<AbstractFunctionDecl>(decl);
12341305
if (isMutatingNonEscapableSelf()) {
12351306
return;
12361307
}
@@ -1263,6 +1334,7 @@ class LifetimeDependenceChecker {
12631334
}
12641335

12651336
void inferUnambiguousInoutParams() {
1337+
auto *afd = cast<AbstractFunctionDecl>(decl);
12661338
if (afd->getParameters()->size() != 1) {
12671339
return;
12681340
}
@@ -1280,6 +1352,7 @@ class LifetimeDependenceChecker {
12801352
}
12811353

12821354
void inferBuiltin() {
1355+
auto *afd = cast<AbstractFunctionDecl>(decl);
12831356
// Normal inout parameter inference works for most generic Builtins.
12841357
inferUnambiguousInoutParams();
12851358
if (!lifetimeDependencies.empty()) {
@@ -1313,8 +1386,12 @@ class LifetimeDependenceChecker {
13131386
};
13141387

13151388
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
1316-
LifetimeDependenceInfo::get(AbstractFunctionDecl *afd) {
1317-
return LifetimeDependenceChecker(afd).checkFuncDecl();
1389+
LifetimeDependenceInfo::get(ValueDecl *decl) {
1390+
if (auto *afd = dyn_cast<AbstractFunctionDecl>(decl)) {
1391+
return LifetimeDependenceChecker(afd).checkFuncDecl();
1392+
}
1393+
auto *eed = cast<EnumElementDecl>(decl);
1394+
return LifetimeDependenceChecker(eed).checkEnumElementDecl();
13181395
}
13191396

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

0 commit comments

Comments
 (0)