Skip to content

LifetimeDependence: implement strict type checking #80064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bd7d3ec
LifetimeDependentInsertion: assert on non-inout parameter dependency
atrick Mar 15, 2025
90021dd
Add a test for unsupported lifetime dependencies on params.
atrick Mar 17, 2025
6adc65b
Comment: getLifetimeDependencies parameter lowering.
atrick Mar 15, 2025
97612ad
LifetimeDependence type checker diagnostic IDs and messages
atrick Mar 15, 2025
5d2c829
LifetimeDependence: implement strict type checking
atrick Mar 15, 2025
edaf04b
Print '@'lifetime(copy x) in the .swiftinterface
atrick Mar 13, 2025
10a5f0c
Add tests for basic LifetimeDependence requirements
atrick Mar 15, 2025
3a383cd
Create a test suite for LifetimeDependence type checking & inferrence
atrick Mar 15, 2025
339cd56
Fix SwiftifyImportMacro to emit @lifetime(copy).
atrick Mar 16, 2025
411a65b
Add @lifetime annotation to RawSpan.init() and Span.init()
atrick Mar 10, 2025
c6116be
Update stdlib source with explicit @lifetime(copy self)
atrick Mar 13, 2025
e919812
Update CXXSpan for strict @lifetime
atrick Mar 15, 2025
88a242f
Update SpanExtras.swift for strict @lifetime checking.
atrick Mar 17, 2025
64a48d0
Update tests for strict @lifetime type checking
atrick Mar 15, 2025
8aa0746
Add a lifetime test for empty initialization.
atrick Mar 17, 2025
8432d2e
Fix C++ interop tests using swift_attr("~Escapable"))
atrick Mar 17, 2025
ed19f7a
SIL parsing: fix a typo for parsing @_unsafeNonEscapableResult
atrick Mar 19, 2025
955d089
CxxSpanReturnThunkBuilder: use _cxxOverrideLifetime(_:copying:)
atrick Mar 19, 2025
d41c4d4
ClangImporter: fix C++ memberwise inits to use @lifetime(immortal)
atrick Mar 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ private func insertParameterDependencies(apply: LifetimeDependentApply, target:

sources.initializeBases(context)

assert(target.value.type.isAddress,
"lifetime-dependent parameter must be 'inout'")

Builder.insert(after: apply.applySite, context) {
insertMarkDependencies(value: target.value, initializer: nil, bases: sources.bases, builder: $0, context)
}
Expand All @@ -285,14 +288,14 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?,
let markDep = builder.createMarkDependence(
value: currentValue, base: base, kind: .Unresolved)

// Address dependencies cannot be represented as SSA values, so it does not make sense to replace any uses of the
// dependent address.
//
// TODO: either (1) insert a separate mark_dependence_addr instruction with no return value, or (2) perform data
// flow to replace all reachable address uses, and if any aren't dominated by base, then insert an extra
// escaping mark_dependence at this apply site that directly uses the mark_dependence [nonescaping] to force
// diagnostics to fail.
if !value.type.isAddress {
if value.type.isAddress {
// Address dependencies cannot be represented as SSA values, so it does not make sense to replace any uses of the
// dependent address.
//
// TODO: insert a separate mark_dependence_addr instruction with no return value and do not update currentValue.
} else {
// TODO: implement non-inout parameter dependencies. This assumes that currentValue is the apply immediately
// preceeding the mark_dependence.
let uses = currentValue.uses.lazy.filter {
if $0.isScopeEndingUse {
return false
Expand Down
100 changes: 69 additions & 31 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -8089,7 +8089,7 @@ ERROR(pack_iteration_where_clause_not_supported, none,


//------------------------------------------------------------------------------
// MARK: Lifetime Dependence Diagnostics
// MARK: Lifetime Dependence Syntax
//------------------------------------------------------------------------------

ERROR(lifetime_dependence_invalid_param_name, none,
Expand All @@ -8108,34 +8108,15 @@ ERROR(lifetime_dependence_cannot_use_kind, none,
ERROR(lifetime_dependence_cannot_use_parsed_scoped_consuming, none,
"invalid use of scoped lifetime dependence with consuming ownership",
())
ERROR(lifetime_dependence_cannot_use_inferred_scoped_consuming, none,
"invalid use of lifetime dependence on an Escapable parameter with "
"consuming ownership",
())
ERROR(lifetime_dependence_invalid_self_ownership, none,
"invalid scoped lifetime dependence on an Escapable self with consuming "
"ownership",
())
ERROR(lifetime_dependence_only_on_function_method_init_result, none,
"lifetime dependence specifiers may only be used on result of "
"functions, methods, initializers", ())
ERROR(lifetime_dependence_invalid_type, none,
"lifetime dependence can only be specified on ~Escapable types", ())
ERROR(lifetime_dependence_cannot_infer_ambiguous_candidate, none,
"cannot infer lifetime dependence %0, multiple parameters qualifiy as a candidate", (StringRef))
ERROR(lifetime_dependence_cannot_infer_no_candidates, none,
"cannot infer lifetime dependence%0, no parameters found that are either "
"~Escapable or Escapable with a borrowing ownership", (StringRef))
ERROR(lifetime_dependence_ctor_non_self_or_nil_return, none,
"expected nil or self as return values in an initializer with "
"lifetime dependent specifiers",
())
ERROR(lifetime_dependence_cannot_be_applied_to_tuple_elt, none,
"lifetime dependence specifiers cannot be applied to tuple elements", ())
ERROR(lifetime_dependence_method_escapable_bitwisecopyable_self, none,
"cannot infer lifetime dependence on a self which is BitwiseCopyable & "
"Escapable",
ERROR(lifetime_dependence_ctor_non_self_or_nil_return, none,
"expected 'nil' or 'self' as return values in an initializer with "
"lifetime dependent specifiers",
())
ERROR(lifetime_dependence_cannot_be_applied_to_tuple_elt, none,
"lifetime dependence specifiers cannot be applied to tuple elements", ())
ERROR(lifetime_dependence_immortal_conflict_name, none,
"conflict between the parameter name and 'immortal' contextual keyword", ())
ERROR(lifetime_dependence_function_type, none,
Expand All @@ -8144,18 +8125,75 @@ ERROR(lifetime_dependence_function_type, none,
ERROR(lifetime_dependence_immortal_alone, none,
"cannot specify any other dependence source along with immortal", ())
ERROR(lifetime_dependence_invalid_inherit_escapable_type, none,
"invalid lifetime dependence on a source of Escapable type, use borrow "
"dependence instead",
())
"cannot copy the lifetime of an Escapable type, use "
"'@lifetime(borrow %0)' instead",
(StringRef))
ERROR(lifetime_dependence_cannot_use_parsed_borrow_consuming, none,
"invalid use of borrow dependence with consuming ownership",
())
ERROR(lifetime_dependence_cannot_use_parsed_borrow_inout, none,
"invalid use of borrow dependence on the same inout parameter",
())
ERROR(lifetime_dependence_duplicate_target, none,
"invalid duplicate target lifetime dependencies on function", ())
ERROR(lifetime_parameter_requires_inout, none,
"lifetime-dependent parameter must be 'inout'", (Identifier))

//------------------------------------------------------------------------------
// MARK: Lifetime Dependence Requirements
//------------------------------------------------------------------------------

ERROR(lifetime_dependence_feature_required_return, none,
"%0 with a ~Escapable result requires "
"'-enable-experimental-feature LifetimeDependence'", (StringRef))
ERROR(lifetime_dependence_feature_required_mutating, none,
"%0 with ~Escapable 'self' requires "
"'-enable-experimental-feature LifetimeDependence'", (StringRef))
ERROR(lifetime_dependence_feature_required_inout, none,
"%0 with a ~Escapable 'inout' parameter requires "
"'-enable-experimental-feature LifetimeDependence'",
// arg list is interchangable with lifetime_dependence_cannot_infer_inout
(StringRef, Identifier))

ERROR(lifetime_dependence_cannot_infer_return, none,
"%0 with a ~Escapable result requires '@lifetime(...)'", (StringRef))
ERROR(lifetime_dependence_cannot_infer_mutating, none,
"%0 with a ~Escapable 'self' requires '@lifetime(self: ...)'", (StringRef))
ERROR(lifetime_dependence_cannot_infer_inout, none,
"%0 with a ~Escapable 'inout' parameter requires '@lifetime(%1: ...)'",
(StringRef, Identifier))

//------------------------------------------------------------------------------
// MARK: Lifetime Dependence Inference - refinements to the requirements above
//------------------------------------------------------------------------------

ERROR(lifetime_dependence_feature_required, none,
"returning ~Escapable type requires '-enable-experimental-feature "
"LifetimeDependence'", ())
ERROR(lifetime_dependence_cannot_infer_return_no_param, none,
"%0 with a ~Escapable result needs a parameter to depend on",
(StringRef))
NOTE(lifetime_dependence_cannot_infer_return_immortal, none,
"'@lifetime(immortal)' can be used to indicate that values produced by "
"this initializer have no lifetime dependencies", ())
ERROR(lifetime_dependence_cannot_infer_bitwisecopyable, none,
"cannot infer lifetime dependence on %0 because '%1' is BitwiseCopyable, "
"specify '@lifetime(borrow self)'",
(StringRef, StringRef))
ERROR(lifetime_dependence_cannot_infer_kind, none,
"cannot infer the lifetime dependence scope on %0 with a ~Escapable "
"parameter, specify '@lifetime(borrow %1)' or '@lifetime(copy %1)'",
(StringRef, StringRef))
ERROR(lifetime_dependence_cannot_infer_scope_ownership, none,
"cannot borrow the lifetime of '%0', which has consuming ownership on %1",
(StringRef, StringRef))

//------------------------------------------------------------------------------
// MARK: Lifetime Dependence Experimental Inference
//------------------------------------------------------------------------------

ERROR(lifetime_dependence_cannot_infer_no_candidates, none,
"cannot infer lifetime dependence%0, no parameters found that are either "
"~Escapable or Escapable with a borrowing ownership", (StringRef))
ERROR(lifetime_dependence_cannot_infer_ambiguous_candidate, none,
"cannot infer lifetime dependence%0, multiple parameters qualify as a candidate", (StringRef))

//===----------------------------------------------------------------------===//
// MARK: Sending
Expand Down
40 changes: 10 additions & 30 deletions include/swift/AST/LifetimeDependence.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,15 @@ class LifetimeEntry final
if (!firstElem) {
result += ", ";
}
if (source.getParsedLifetimeDependenceKind() ==
ParsedLifetimeDependenceKind::Scope) {
switch (source.getParsedLifetimeDependenceKind()) {
case ParsedLifetimeDependenceKind::Scope:
result += "borrow ";
break;
case ParsedLifetimeDependenceKind::Inherit:
result += "copy ";
break;
default:
break;
}
result += source.getString();
firstElem = false;
Expand All @@ -227,32 +233,6 @@ class LifetimeDependenceInfo {

unsigned targetIndex;

static LifetimeDependenceInfo getForIndex(AbstractFunctionDecl *afd,
unsigned targetIndex,
unsigned sourceIndex,
LifetimeDependenceKind kind);

/// Builds LifetimeDependenceInfo from @lifetime attribute
static std::optional<ArrayRef<LifetimeDependenceInfo>>
fromLifetimeAttribute(AbstractFunctionDecl *afd);

/// Infer LifetimeDependenceInfo on result
static std::optional<LifetimeDependenceInfo> infer(AbstractFunctionDecl *afd);

/// Infer LifetimeDependenceInfo on setter
static std::optional<LifetimeDependenceInfo>
inferSetter(AbstractFunctionDecl *afd);

/// Infer LifetimeDependenceInfo on mutating self
static std::optional<LifetimeDependenceInfo>
inferMutatingSelf(AbstractFunctionDecl *afd);

/// Builds LifetimeDependenceInfo from SIL function type
static std::optional<LifetimeDependenceInfo>
fromDependsOn(LifetimeDependentTypeRepr *lifetimeDependentRepr,
unsigned targetIndex, ArrayRef<SILParameterInfo> params,
DeclContext *dc);

public:
LifetimeDependenceInfo(IndexSubset *inheritLifetimeParamIndices,
IndexSubset *scopeLifetimeParamIndices,
Expand Down Expand Up @@ -350,8 +330,8 @@ class LifetimeDependenceInfo {

/// Builds LifetimeDependenceInfo from SIL
static std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
get(FunctionTypeRepr *funcRepr, ArrayRef<SILParameterInfo> params,
ArrayRef<SILResultInfo> results, DeclContext *dc);
getFromSIL(FunctionTypeRepr *funcRepr, ArrayRef<SILParameterInfo> params,
ArrayRef<SILResultInfo> results, DeclContext *dc);

bool operator==(const LifetimeDependenceInfo &other) const {
return this->isImmortal() == other.isImmortal() &&
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -5574,6 +5574,8 @@ class SILFunctionType final
return NumLifetimeDependencies != 0;
}

// Return lowered lifetime dependencies, which has remapped parameter indices
// relative to the original FunctionType.
ArrayRef<LifetimeDependenceInfo> getLifetimeDependencies() const {
if (!hasLifetimeDependencies())
return std::nullopt;
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ namespace swift {
bool EnableRequirementMachineOpaqueArchetypes = false;

/// Enable implicit lifetime dependence for ~Escapable return types.
bool EnableExperimentalLifetimeDependenceInference = true;
bool EnableExperimentalLifetimeDependenceInference = false;

/// Skips decls that cannot be referenced externally.
bool SkipNonExportableDecls = false;
Expand Down
Loading