Skip to content

Commit

Permalink
Update lifetime dependence syntax and inference as per changes in the…
Browse files Browse the repository at this point in the history
… pitch

Pitch - apple/swift-evolution#2305

Changes highlights:

dependsOn(paramName) and dependsOn(scoped argName) syntax

dependsOn(paramName) -> copy lifetime dependence for all parameters/self except
                         when we have Escapable parameters/self, we assign scope
                         lifetime dependence.

Allow lifetime dependence on parameters without ownership modifier.

Always infer copy lifetime dependence except when we have
Escapable parameters/self, we infer scope lifetime dependence.

Allow lifetime dependence inference on parameters without ownership modifier.
  • Loading branch information
meg-gupta committed Mar 24, 2024
1 parent e3a11ff commit b5ca933
Show file tree
Hide file tree
Showing 35 changed files with 386 additions and 352 deletions.
27 changes: 15 additions & 12 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -7845,25 +7845,31 @@ ERROR(lifetime_dependence_invalid_param_name, none,
"invalid parameter name specified %0", (Identifier))
ERROR(lifetime_dependence_invalid_param_index, none,
"invalid parameter index specified %0", (unsigned))
ERROR(lifetime_dependence_invalid_self, none,
"invalid lifetime dependence specifier, self is valid in non-static "
"methods only", ())
ERROR(lifetime_dependence_invalid_self_in_static, none,
"invalid lifetime dependence specifier on non-existent self", ())
ERROR(lifetime_dependence_invalid_self_in_init, none,
"invalid lifetime dependence on self in an initializer", ())
ERROR(lifetime_dependence_duplicate_param_id, none,
"duplicate lifetime dependence specifier", ())
ERROR(lifetime_dependence_cannot_use_kind, none,
"invalid use of %0 lifetime dependence for %1 ownership",
(StringRef, StringRef))
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_return_type, none,
"lifetime dependence can only be specified on ~Escapable results", ())
ERROR(lifetime_dependence_missing_ownership_modifier, none,
"lifetime dependence can only be specified on parameters with ownership "
"modifiers (borrowing, consuming, inout)", ())
ERROR(lifetime_dependence_cannot_infer_wo_ownership_modifier_on_method, none,
"cannot infer lifetime dependence, specify ownership modifier for the "
"method", ())
ERROR(lifetime_dependence_cannot_infer_ambiguous_candidate, none,
"cannot infer lifetime dependence, multiple ~Escapable or ~Copyable "
"parameters with ownership modifiers, specify explicit lifetime "
Expand All @@ -7874,9 +7880,6 @@ ERROR(lifetime_dependence_cannot_infer_no_candidates, none,
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_use_infer, none,
"invalid use of %0 lifetime dependence on Escapable type",
(StringRef))
ERROR(lifetime_dependence_on_bitwise_copyable, none,
"invalid lifetime dependence on bitwise copyable type", ())
ERROR(lifetime_dependence_cannot_infer_implicit_init, none,
Expand Down
54 changes: 27 additions & 27 deletions include/swift/AST/LifetimeDependence.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,22 @@ class AbstractFunctionDecl;
class LifetimeDependentReturnTypeRepr;
class SILParameterInfo;

enum class LifetimeDependenceKind : uint8_t {
Copy = 0,
Consume,
Borrow,
Mutate
enum class ParsedLifetimeDependenceKind : uint8_t {
Default = 0,
Scope,
Inherit // Only used with deserialized decls
};

enum class LifetimeDependenceKind : uint8_t { Inherit = 0, Scope };

class LifetimeDependenceSpecifier {
public:
enum class SpecifierKind { Named, Ordered, Self };

private:
SourceLoc loc;
SpecifierKind specifierKind;
LifetimeDependenceKind lifetimeDependenceKind;
ParsedLifetimeDependenceKind parsedLifetimeDependenceKind;
union Value {
struct {
Identifier name;
Expand All @@ -61,35 +62,36 @@ class LifetimeDependenceSpecifier {
Value() {}
} value;

LifetimeDependenceSpecifier(SourceLoc loc, SpecifierKind specifierKind,
LifetimeDependenceKind lifetimeDependenceKind,
Value value)
LifetimeDependenceSpecifier(
SourceLoc loc, SpecifierKind specifierKind,
ParsedLifetimeDependenceKind parsedLifetimeDependenceKind, Value value)
: loc(loc), specifierKind(specifierKind),
lifetimeDependenceKind(lifetimeDependenceKind), value(value) {}
parsedLifetimeDependenceKind(parsedLifetimeDependenceKind),
value(value) {}

public:
static LifetimeDependenceSpecifier getNamedLifetimeDependenceSpecifier(
SourceLoc loc, LifetimeDependenceKind kind, Identifier name) {
SourceLoc loc, ParsedLifetimeDependenceKind kind, Identifier name) {
return {loc, SpecifierKind::Named, kind, name};
}

static LifetimeDependenceSpecifier getOrderedLifetimeDependenceSpecifier(
SourceLoc loc, LifetimeDependenceKind kind, unsigned index) {
SourceLoc loc, ParsedLifetimeDependenceKind kind, unsigned index) {
return {loc, SpecifierKind::Ordered, kind, index};
}

static LifetimeDependenceSpecifier
getSelfLifetimeDependenceSpecifier(SourceLoc loc,
LifetimeDependenceKind kind) {
ParsedLifetimeDependenceKind kind) {
return {loc, SpecifierKind::Self, kind, {}};
}

SourceLoc getLoc() const { return loc; }

SpecifierKind getSpecifierKind() const { return specifierKind; }

LifetimeDependenceKind getLifetimeDependenceKind() const {
return lifetimeDependenceKind;
ParsedLifetimeDependenceKind getParsedLifetimeDependenceKind() const {
return parsedLifetimeDependenceKind;
}

Identifier getName() const {
Expand All @@ -114,19 +116,17 @@ class LifetimeDependenceSpecifier {
llvm_unreachable("Invalid LifetimeDependenceSpecifier::SpecifierKind");
}

StringRef getLifetimeDependenceKindString() const {
switch (lifetimeDependenceKind) {
case LifetimeDependenceKind::Borrow:
return "_borrow";
case LifetimeDependenceKind::Consume:
return "_consume";
case LifetimeDependenceKind::Copy:
return "_copy";
case LifetimeDependenceKind::Mutate:
return "_mutate";
std::string getLifetimeDependenceSpecifierString() const {
switch (parsedLifetimeDependenceKind) {
case ParsedLifetimeDependenceKind::Default:
return "dependsOn(" + getParamString() + ")";
case ParsedLifetimeDependenceKind::Scope:
return "dependsOn(scoped " + getParamString() + ")";
case ParsedLifetimeDependenceKind::Inherit:
return "dependsOn(inherited " + getParamString() + ")";
}
llvm_unreachable(
"Invalid LifetimeDependenceSpecifier::LifetimeDependenceKind");
"Invalid LifetimeDependenceSpecifier::ParsedLifetimeDependenceKind");
}
};

Expand All @@ -137,7 +137,7 @@ class LifetimeDependenceInfo {

static LifetimeDependenceInfo getForParamIndex(AbstractFunctionDecl *afd,
unsigned index,
ValueOwnership ownership);
LifetimeDependenceKind kind);

public:
LifetimeDependenceInfo()
Expand Down
27 changes: 15 additions & 12 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ class Parser {
return true;
if (Context.LangOpts.hasFeature(Feature::NonescapableTypes) &&
(Tok.isContextualKeyword("_resultDependsOn") ||
Tok.isLifetimeDependenceToken(isInSILMode())))
isLifetimeDependenceToken()))
return true;
return false;
}
Expand All @@ -1224,23 +1224,26 @@ class Parser {
consumeToken();
}

bool isLifetimeDependenceToken() {
if (!isInSILMode()) {
return Tok.isContextualKeyword("dependsOn");
}
return Tok.isContextualKeyword("_inherit") ||
Tok.isContextualKeyword("_scope");
}

bool canHaveParameterSpecifierContextualKeyword() {
// The parameter specifiers like `isolated`, `consuming`, `borrowing` are
// also valid identifiers and could be the name of a type. Check whether
// the following token is something that can introduce a type. Thankfully
// none of these tokens overlap with the set of tokens that can follow an
// identifier in a type production.
return Tok.is(tok::identifier)
&& peekToken().isAny(tok::at_sign,
tok::kw_inout,
tok::l_paren,
tok::identifier,
tok::l_square,
tok::kw_Any,
tok::kw_Self,
tok::kw__,
tok::kw_var,
tok::kw_let);
return (Tok.is(tok::identifier) &&
peekToken().isAny(tok::at_sign, tok::kw_inout, tok::l_paren,
tok::identifier, tok::l_square, tok::kw_Any,
tok::kw_Self, tok::kw__, tok::kw_var,
tok::kw_let)) ||
isLifetimeDependenceToken();
}

struct ParsedTypeAttributeList {
Expand Down
10 changes: 0 additions & 10 deletions include/swift/Parse/Token.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,16 +267,6 @@ class Token {
return MultilineString;
}

bool isLifetimeDependenceToken(bool isInSILMode) const {
return isContextualKeyword("_copy") || isContextualKeyword("_consume") ||
isContextualKeyword("_borrow") || isContextualKeyword("_mutate") ||
// SIL tests are currently written with _inherit/_scope
// Once we have dependsOn()/dependsOn(borrowed:) other tokens go
// away.
(isInSILMode &&
(isContextualKeyword("_inherit") || isContextualKeyword("_scope")));
}

/// Count of extending escaping '#'.
unsigned getCustomDelimiterLen() const {
return CustomDelimiterLen;
Expand Down
3 changes: 1 addition & 2 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3535,8 +3535,7 @@ class PrintTypeRepr : public TypeReprVisitor<PrintTypeRepr, void, StringRef>,
for (auto &dep : T->getLifetimeDependencies()) {
printFieldRaw(
[&](raw_ostream &out) {
out << dep.getLifetimeDependenceKindString() << "(";
out << dep.getParamString() << ")";
out << " " << dep.getLifetimeDependenceSpecifierString() << " ";
},
"");
}
Expand Down
10 changes: 2 additions & 8 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3263,20 +3263,14 @@ void ASTMangler::appendParameterTypeListElement(

void ASTMangler::appendLifetimeDependenceKind(LifetimeDependenceKind kind,
bool isSelfDependence) {
// If we converge on dependsOn(borrowed: paramName)/dependsOn(paramName)
// syntax, this can be a single case value check.
if (kind == LifetimeDependenceKind::Borrow ||
kind == LifetimeDependenceKind::Mutate) {
if (kind == LifetimeDependenceKind::Scope) {
if (isSelfDependence) {
appendOperator("YLs");
} else {
appendOperator("Yls");
}
} else {
// If we converge on dependsOn(borrowed: paramName)/dependsOn(paramName)
// syntax, this can be a single case value check.
assert(kind == LifetimeDependenceKind::Copy ||
kind == LifetimeDependenceKind::Consume);
assert(kind == LifetimeDependenceKind::Inherit);
if (isSelfDependence) {
appendOperator("YLi");
} else {
Expand Down
6 changes: 2 additions & 4 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4137,8 +4137,7 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
if (auto *typeRepr = dyn_cast_or_null<LifetimeDependentReturnTypeRepr>(
decl->getResultTypeRepr())) {
for (auto &dep : typeRepr->getLifetimeDependencies()) {
Printer << " " << dep.getLifetimeDependenceKindString() << "(";
Printer << dep.getParamString() << ") ";
Printer << " " << dep.getLifetimeDependenceSpecifierString() << " ";
}
}
}
Expand Down Expand Up @@ -4378,8 +4377,7 @@ void PrintAST::visitConstructorDecl(ConstructorDecl *decl) {
auto *typeRepr =
cast<LifetimeDependentReturnTypeRepr>(decl->getResultTypeRepr());
for (auto &dep : typeRepr->getLifetimeDependencies()) {
Printer << dep.getLifetimeDependenceKindString() << "(";
Printer << dep.getParamString() << ") ";
Printer << dep.getLifetimeDependenceSpecifierString() << " ";
}
// TODO: Handle failable initializers with lifetime dependent returns
Printer << "Self";
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/TypeRepr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,9 +692,9 @@ SourceLoc LifetimeDependentReturnTypeRepr::getLocImpl() const {

void LifetimeDependentReturnTypeRepr::printImpl(
ASTPrinter &Printer, const PrintOptions &Opts) const {
Printer << " ";
for (auto &dep : getLifetimeDependencies()) {
Printer << dep.getLifetimeDependenceKindString() << "(";
Printer << dep.getParamString() << ")";
Printer << dep.getLifetimeDependenceSpecifierString() << " ";
}

printTypeRepr(getBase(), Printer, Opts);
Expand Down
53 changes: 31 additions & 22 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5057,34 +5057,33 @@ ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result,
llvm_unreachable("bad attribute kind");
}

static std::optional<LifetimeDependenceKind>
getLifetimeDependenceKind(const Token &T) {
if (T.isContextualKeyword("_copy") || T.isContextualKeyword("_inherit")) {
return LifetimeDependenceKind::Copy;
static ParsedLifetimeDependenceKind getSILLifetimeDependenceKind(const Token &T) {
if (T.isContextualKeyword("_inherit")) {
return ParsedLifetimeDependenceKind::Inherit;
}
if (T.isContextualKeyword("_consume")) {
return LifetimeDependenceKind::Consume;
}
if (T.isContextualKeyword("_borrow") || T.isContextualKeyword("_scope")) {
return LifetimeDependenceKind::Borrow;
}
if (T.isContextualKeyword("_mutate")) {
return LifetimeDependenceKind::Mutate;
}
return std::nullopt;
assert(T.isContextualKeyword("_scope"));
return ParsedLifetimeDependenceKind::Scope;
}

ParserStatus Parser::parseLifetimeDependenceSpecifiers(
SmallVectorImpl<LifetimeDependenceSpecifier> &specifierList) {
ParserStatus status;
// TODO: Add fixits for diagnostics in this function.
do {
auto lifetimeDependenceKind = getLifetimeDependenceKind(Tok);
if (!lifetimeDependenceKind.has_value()) {
if (!isLifetimeDependenceToken()) {
break;
}
// consume the lifetime dependence kind
consumeToken();

auto lifetimeDependenceKind = ParsedLifetimeDependenceKind::Default;

if (!isInSILMode()) {
// consume dependsOn
consumeToken();
} else {
lifetimeDependenceKind = getSILLifetimeDependenceKind(Tok);
// consume _inherit or _scope
consumeToken();
}

if (!Tok.isFollowingLParen()) {
diagnose(Tok, diag::expected_lparen_after_lifetime_dependence);
Expand All @@ -5093,6 +5092,16 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers(
}
// consume the l_paren
auto lParenLoc = consumeToken();

if (!isInSILMode()) {
// look for optional "scoped"
if (Tok.isContextualKeyword("scoped")) {
lifetimeDependenceKind = ParsedLifetimeDependenceKind::Scope;
// consume scoped
consumeToken();
}
}

SourceLoc rParenLoc;
bool foundParamId = false;
status = parseList(
Expand All @@ -5108,7 +5117,7 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers(
specifierList.push_back(
LifetimeDependenceSpecifier::
getNamedLifetimeDependenceSpecifier(
paramLoc, *lifetimeDependenceKind, paramName));
paramLoc, lifetimeDependenceKind, paramName));
break;
}
case tok::integer_literal: {
Expand All @@ -5123,14 +5132,14 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers(
specifierList.push_back(
LifetimeDependenceSpecifier::
getOrderedLifetimeDependenceSpecifier(
paramLoc, *lifetimeDependenceKind, paramNum));
paramLoc, lifetimeDependenceKind, paramNum));
break;
}
case tok::kw_self: {
auto paramLoc = consumeToken(tok::kw_self);
specifierList.push_back(
LifetimeDependenceSpecifier::getSelfLifetimeDependenceSpecifier(
paramLoc, *lifetimeDependenceKind));
paramLoc, lifetimeDependenceKind));
break;
}
default:
Expand Down Expand Up @@ -5465,7 +5474,7 @@ ParserStatus Parser::ParsedTypeAttributeList::slowParse(Parser &P) {
continue;
}

if (Tok.isLifetimeDependenceToken(P.isInSILMode())) {
if (P.isLifetimeDependenceToken()) {
if (!P.Context.LangOpts.hasFeature(Feature::NonescapableTypes)) {
P.diagnose(Tok, diag::requires_experimental_feature,
"lifetime dependence specifier", false,
Expand Down

0 comments on commit b5ca933

Please sign in to comment.