Skip to content

Update lifetime dependence syntax and inference #72506

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 2 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading