Skip to content

Implement consuming and borrowing declaration-level modifiers from SE-0377. #63990

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 1 commit into from
Mar 2, 2023
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
8 changes: 2 additions & 6 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -7174,7 +7174,9 @@ class OperatorDecl;
enum class SelfAccessKind : uint8_t {
NonMutating,
Mutating,
LegacyConsuming,
Consuming,
Borrowing,
};

/// Diagnostic printing of \c SelfAccessKind.
Expand Down Expand Up @@ -7293,12 +7295,6 @@ class FuncDecl : public AbstractFunctionDecl {
bool isMutating() const {
return getSelfAccessKind() == SelfAccessKind::Mutating;
}
bool isNonMutating() const {
return getSelfAccessKind() == SelfAccessKind::NonMutating;
}
bool isConsuming() const {
return getSelfAccessKind() == SelfAccessKind::Consuming;
}
bool isCallAsFunctionMethod() const;

bool isMainTypeMainMethod() const;
Expand Down
8 changes: 7 additions & 1 deletion lib/APIDigester/ModuleAnalyzerNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1538,13 +1538,19 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD)
case SelfAccessKind::Mutating:
FuncSelfKind = "Mutating";
break;
case SelfAccessKind::Consuming:
case SelfAccessKind::LegacyConsuming:
// FIXME: Stay consistent with earlier digests that had underscores here.
FuncSelfKind = "__Consuming";
break;
case SelfAccessKind::NonMutating:
FuncSelfKind = "NonMutating";
break;
case SelfAccessKind::Consuming:
FuncSelfKind = "Consuming";
break;
case SelfAccessKind::Borrowing:
FuncSelfKind = "Borrowing";
break;
}
}

Expand Down
6 changes: 5 additions & 1 deletion lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3487,8 +3487,12 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD,

auto flags = ParameterTypeFlags().withIsolated(isIsolated);
switch (selfAccess) {
case SelfAccessKind::LegacyConsuming:
case SelfAccessKind::Consuming:
flags = flags.withOwned(true);
flags = flags.withValueOwnership(ValueOwnership::Owned);
break;
case SelfAccessKind::Borrowing:
flags = flags.withValueOwnership(ValueOwnership::Shared);
break;
case SelfAccessKind::Mutating:
flags = flags.withInOut(true);
Expand Down
12 changes: 11 additions & 1 deletion lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,9 @@ void PrintAST::printAttributes(const Decl *D) {
if (isa<FuncDecl>(D)) {
Options.ExcludeAttrList.push_back(DAK_Mutating);
Options.ExcludeAttrList.push_back(DAK_NonMutating);
Options.ExcludeAttrList.push_back(DAK_LegacyConsuming);
Options.ExcludeAttrList.push_back(DAK_Consuming);
Options.ExcludeAttrList.push_back(DAK_Borrowing);
}

D->getAttrs().print(Printer, Options, D);
Expand Down Expand Up @@ -2063,9 +2065,17 @@ void PrintAST::printSelfAccessKindModifiersIfNeeded(const FuncDecl *FD) {
!Options.excludeAttrKind(DAK_NonMutating))
Printer.printKeyword("nonmutating", Options, " ");
break;
case SelfAccessKind::LegacyConsuming:
if (!Options.excludeAttrKind(DAK_LegacyConsuming))
Printer.printKeyword("__consuming", Options, " ");
break;
case SelfAccessKind::Consuming:
if (!Options.excludeAttrKind(DAK_Consuming))
Printer.printKeyword("__consuming", Options, " ");
Printer.printKeyword("consuming", Options, " ");
break;
case SelfAccessKind::Borrowing:
if (!Options.excludeAttrKind(DAK_Borrowing))
Printer.printKeyword("borrowing", Options, " ");
break;
}
}
Expand Down
4 changes: 3 additions & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,9 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS,
switch (SAK) {
case SelfAccessKind::NonMutating: return OS << "'nonmutating'";
case SelfAccessKind::Mutating: return OS << "'mutating'";
case SelfAccessKind::Consuming: return OS << "'__consuming'";
case SelfAccessKind::LegacyConsuming: return OS << "'__consuming'";
case SelfAccessKind::Consuming: return OS << "'consuming'";
case SelfAccessKind::Borrowing: return OS << "'borrowing'";
}
llvm_unreachable("Unknown SelfAccessKind");
}
Expand Down
4 changes: 4 additions & 0 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6765,7 +6765,11 @@ static bool parseAccessorIntroducer(Parser &P,
} else if (P.Tok.isContextualKeyword("nonmutating")) {
P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_NonMutating);
} else if (P.Tok.isContextualKeyword("__consuming")) {
P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_LegacyConsuming);
} else if (P.Tok.isContextualKeyword("consuming")) {
P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Consuming);
} else if (P.Tok.isContextualKeyword("borrowing")) {
P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Borrowing);
}
}

Expand Down
9 changes: 4 additions & 5 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5098,11 +5098,10 @@ bool SILGenModule::shouldEmitSelfAsRValue(FuncDecl *fn, CanType selfType) {
switch (fn->getSelfAccessKind()) {
case SelfAccessKind::Mutating:
return false;
case SelfAccessKind::Consuming:
return true;
case SelfAccessKind::NonMutating:
// TODO: borrow 'self' for nonmutating methods on methods on value types.
// return selfType->hasReferenceSemantics();
case SelfAccessKind::LegacyConsuming:
case SelfAccessKind::Consuming:
case SelfAccessKind::Borrowing:
return true;
}
llvm_unreachable("bad self-access kind");
Expand All @@ -5114,7 +5113,7 @@ bool SILGenModule::isNonMutatingSelfIndirect(SILDeclRef methodRef) {
return false;

assert(method->getDeclContext()->isTypeContext());
assert(method->isNonMutating() || method->isConsuming());
assert(!method->isMutating());

auto fnType = M.Types.getConstantFunctionType(TypeExpansionContext::minimal(),
methodRef);
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/DerivedConformanceDifferentiable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ static bool canInvokeMoveByOnProperty(
if (!witness)
return false;
auto *decl = cast<FuncDecl>(witness.getDecl());
return decl->isNonMutating();
return !decl->isMutating();
}

/// Get the stored properties of a nominal type that are relevant for
Expand Down
45 changes: 39 additions & 6 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
void visitMutationAttr(DeclAttribute *attr);
void visitMutatingAttr(MutatingAttr *attr) { visitMutationAttr(attr); }
void visitNonMutatingAttr(NonMutatingAttr *attr) { visitMutationAttr(attr); }
void visitBorrowingAttr(BorrowingAttr *attr) { visitMutationAttr(attr); }
void visitConsumingAttr(ConsumingAttr *attr) { visitMutationAttr(attr); }
void visitLegacyConsumingAttr(LegacyConsumingAttr *attr) { visitMutationAttr(attr); }
void visitDynamicAttr(DynamicAttr *attr);

void visitIndirectAttr(IndirectAttr *attr) {
Expand Down Expand Up @@ -443,15 +445,21 @@ void AttributeChecker::visitMutationAttr(DeclAttribute *attr) {

SelfAccessKind attrModifier;
switch (attr->getKind()) {
case DeclAttrKind::DAK_Consuming:
attrModifier = SelfAccessKind::Consuming;
case DeclAttrKind::DAK_LegacyConsuming:
attrModifier = SelfAccessKind::LegacyConsuming;
break;
case DeclAttrKind::DAK_Mutating:
attrModifier = SelfAccessKind::Mutating;
break;
case DeclAttrKind::DAK_NonMutating:
attrModifier = SelfAccessKind::NonMutating;
break;
case DeclAttrKind::DAK_Consuming:
attrModifier = SelfAccessKind::Consuming;
break;
case DeclAttrKind::DAK_Borrowing:
attrModifier = SelfAccessKind::Borrowing;
break;
default:
llvm_unreachable("unhandled attribute kind");
}
Expand All @@ -462,22 +470,33 @@ void AttributeChecker::visitMutationAttr(DeclAttribute *attr) {
// 'mutating' and 'nonmutating' are not valid on types
// with reference semantics.
if (contextTy->hasReferenceSemantics()) {
if (attrModifier != SelfAccessKind::Consuming) {
switch (attrModifier) {
case SelfAccessKind::Consuming:
case SelfAccessKind::LegacyConsuming:
case SelfAccessKind::Borrowing:
// It's still OK to specify the ownership convention of methods in
// classes.
break;

case SelfAccessKind::Mutating:
case SelfAccessKind::NonMutating:
diagnoseAndRemoveAttr(attr, diag::mutating_invalid_classes,
attrModifier, FD->getDescriptiveKind(),
DC->getSelfProtocolDecl() != nullptr);
break;
}
}
} else {
diagnoseAndRemoveAttr(attr, diag::mutating_invalid_global_scope,
attrModifier);
}

// Verify we don't have more than one of mutating, nonmutating,
// and __consuming.
// Verify we don't have more than one ownership specifier.
if ((FD->getAttrs().hasAttribute<MutatingAttr>() +
FD->getAttrs().hasAttribute<NonMutatingAttr>() +
FD->getAttrs().hasAttribute<ConsumingAttr>()) > 1) {
FD->getAttrs().hasAttribute<LegacyConsumingAttr>() +
FD->getAttrs().hasAttribute<ConsumingAttr>() +
FD->getAttrs().hasAttribute<BorrowingAttr>()) > 1) {
if (auto *NMA = FD->getAttrs().getAttribute<NonMutatingAttr>()) {
if (attrModifier != SelfAccessKind::NonMutating) {
diagnoseAndRemoveAttr(NMA, diag::functions_mutating_and_not,
Expand All @@ -492,12 +511,26 @@ void AttributeChecker::visitMutationAttr(DeclAttribute *attr) {
}
}

if (auto *CSA = FD->getAttrs().getAttribute<LegacyConsumingAttr>()) {
if (attrModifier != SelfAccessKind::LegacyConsuming) {
diagnoseAndRemoveAttr(CSA, diag::functions_mutating_and_not,
SelfAccessKind::LegacyConsuming, attrModifier);
}
}

if (auto *CSA = FD->getAttrs().getAttribute<ConsumingAttr>()) {
if (attrModifier != SelfAccessKind::Consuming) {
diagnoseAndRemoveAttr(CSA, diag::functions_mutating_and_not,
SelfAccessKind::Consuming, attrModifier);
}
}

if (auto *BSA = FD->getAttrs().getAttribute<BorrowingAttr>()) {
if (attrModifier != SelfAccessKind::Borrowing) {
diagnoseAndRemoveAttr(BSA, diag::functions_mutating_and_not,
SelfAccessKind::Borrowing, attrModifier);
}
}
}

// Verify that we don't have a static function.
Expand Down
29 changes: 25 additions & 4 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1670,8 +1670,12 @@ SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
return SelfAccessKind::Mutating;
} else if (FD->getAttrs().hasAttribute<NonMutatingAttr>()) {
return SelfAccessKind::NonMutating;
} else if (FD->getAttrs().hasAttribute<LegacyConsumingAttr>()) {
return SelfAccessKind::LegacyConsuming;
} else if (FD->getAttrs().hasAttribute<ConsumingAttr>()) {
return SelfAccessKind::Consuming;
} else if (FD->getAttrs().hasAttribute<BorrowingAttr>()) {
return SelfAccessKind::Borrowing;
}

if (auto *AD = dyn_cast<AccessorDecl>(FD)) {
Expand Down Expand Up @@ -2167,12 +2171,29 @@ ParamSpecifierRequest::evaluate(Evaluator &evaluator,
auto *dc = param->getDeclContext();

if (param->isSelfParameter()) {
auto selfParam = computeSelfParam(cast<AbstractFunctionDecl>(dc),
auto afd = cast<AbstractFunctionDecl>(dc);
auto selfParam = computeSelfParam(afd,
/*isInitializingCtor*/true,
/*wantDynamicSelf*/false);
return (selfParam.getParameterFlags().isInOut()
? ParamSpecifier::InOut
: ParamSpecifier::Default);
if (auto fd = dyn_cast<FuncDecl>(afd)) {
switch (fd->getSelfAccessKind()) {
case SelfAccessKind::LegacyConsuming:
return ParamSpecifier::LegacyOwned;
case SelfAccessKind::Consuming:
return ParamSpecifier::Consuming;
case SelfAccessKind::Borrowing:
return ParamSpecifier::Borrowing;
case SelfAccessKind::Mutating:
return ParamSpecifier::InOut;
case SelfAccessKind::NonMutating:
return ParamSpecifier::Default;
}
llvm_unreachable("nonexhaustive switch");
} else {
return (selfParam.getParameterFlags().isInOut()
? ParamSpecifier::InOut
: ParamSpecifier::Default);
}
}

if (auto *accessor = dyn_cast<AccessorDecl>(dc)) {
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/TypeCheckDeclOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,7 @@ namespace {
UNINTERESTING_ATTR(Alignment)
UNINTERESTING_ATTR(AlwaysEmitIntoClient)
UNINTERESTING_ATTR(Borrowed)
UNINTERESTING_ATTR(Borrowing)
UNINTERESTING_ATTR(CDecl)
UNINTERESTING_ATTR(Consuming)
UNINTERESTING_ATTR(Documentation)
Expand Down Expand Up @@ -1519,6 +1520,7 @@ namespace {
UNINTERESTING_ATTR(MoveOnly)
UNINTERESTING_ATTR(FixedLayout)
UNINTERESTING_ATTR(Lazy)
UNINTERESTING_ATTR(LegacyConsuming)
UNINTERESTING_ATTR(LLDBDebuggerFunction)
UNINTERESTING_ATTR(Mutating)
UNINTERESTING_ATTR(NonMutating)
Expand Down
4 changes: 3 additions & 1 deletion lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3591,8 +3591,10 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,

// FIXME: Once we support move-only types in generics, remove this if the
// conforming type is move-only. Until then, don't suggest printing
// __consuming on a protocol requirement.
// ownership modifiers on a protocol requirement.
Options.ExcludeAttrList.push_back(DAK_LegacyConsuming);
Options.ExcludeAttrList.push_back(DAK_Consuming);
Options.ExcludeAttrList.push_back(DAK_Borrowing);

Options.FunctionBody = [&](const ValueDecl *VD, ASTPrinter &Printer) {
Printer << " {";
Expand Down
20 changes: 15 additions & 5 deletions lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1324,13 +1324,23 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
// The 'self' parameter must be owned (aka "consuming").
if (!diagnosed) {
bool isConsuming = false;
if (auto *funcDecl = dyn_cast<FuncDecl>(fn))
isConsuming = funcDecl->isConsuming();
else if (auto *accessor = dyn_cast<AccessorDecl>(fn))
isConsuming = accessor->isConsuming();
else if (isa<ConstructorDecl>(fn))
if (auto *funcDecl = dyn_cast<FuncDecl>(fn)) {
switch (funcDecl->getSelfAccessKind()) {
case SelfAccessKind::LegacyConsuming:
case SelfAccessKind::Consuming:
isConsuming = true;
break;

case SelfAccessKind::Borrowing:
case SelfAccessKind::NonMutating:
case SelfAccessKind::Mutating:
isConsuming = false;
break;
}
} else if (isa<ConstructorDecl>(fn)) {
// constructors are implicitly "consuming" of the self instance.
isConsuming = true;
}

if (!isConsuming) {
ctx.Diags.diagnose(FS->getForgetLoc(),
Expand Down
4 changes: 4 additions & 0 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2508,8 +2508,12 @@ getActualSelfAccessKind(uint8_t raw) {
return swift::SelfAccessKind::NonMutating;
case serialization::SelfAccessKind::Mutating:
return swift::SelfAccessKind::Mutating;
case serialization::SelfAccessKind::LegacyConsuming:
return swift::SelfAccessKind::LegacyConsuming;
case serialization::SelfAccessKind::Consuming:
return swift::SelfAccessKind::Consuming;
case serialization::SelfAccessKind::Borrowing:
return swift::SelfAccessKind::Borrowing;
}
return None;
}
Expand Down
6 changes: 4 additions & 2 deletions lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 746; // `consuming` and `borrowing` parameter modifiers
const uint16_t SWIFTMODULE_VERSION_MINOR = 747; // `consuming` and `borrowing` decl modifiers

/// A standard hash seed used for all string hashes in a serialized module.
///
Expand Down Expand Up @@ -405,9 +405,11 @@ using MetatypeRepresentationField = BCFixed<2>;
enum class SelfAccessKind : uint8_t {
NonMutating = 0,
Mutating,
LegacyConsuming,
Consuming,
Borrowing
};
using SelfAccessKindField = BCFixed<2>;
using SelfAccessKindField = BCFixed<3>;

/// Translates an operator decl fixity to a Serialization fixity, whose values
/// are guaranteed to be stable.
Expand Down
4 changes: 4 additions & 0 deletions lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2216,8 +2216,12 @@ getStableSelfAccessKind(swift::SelfAccessKind MM) {
return serialization::SelfAccessKind::NonMutating;
case swift::SelfAccessKind::Mutating:
return serialization::SelfAccessKind::Mutating;
case swift::SelfAccessKind::LegacyConsuming:
return serialization::SelfAccessKind::LegacyConsuming;
case swift::SelfAccessKind::Consuming:
return serialization::SelfAccessKind::Consuming;
case swift::SelfAccessKind::Borrowing:
return serialization::SelfAccessKind::Borrowing;
}

llvm_unreachable("Unhandled StaticSpellingKind in switch.");
Expand Down
Loading