Skip to content

[region-isolation] Add support for transferring parameters. #70836

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 5 commits into from
Jan 19, 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
1 change: 1 addition & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,7 @@ enum ENUM_EXTENSIBILITY_ATTR(open) BridgedAttributedTypeSpecifier : size_t {
BridgedAttributedTypeSpecifierConst,
BridgedAttributedTypeSpecifierIsolated,
BridgedAttributedTypeSpecifierResultDependsOn,
BridgedAttributedTypeSpecifierTransferring,
};

SWIFT_NAME("BridgedSimpleIdentTypeRepr.createParsed(_:loc:name:)")
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,10 @@ SIMPLE_DECL_ATTR(_noObjCBridging, NoObjCBridging,
CONTEXTUAL_SIMPLE_DECL_ATTR(_resultDependsOn, ResultDependsOn,
OnParam | DeclModifier | UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
156)
CONTEXTUAL_SIMPLE_DECL_ATTR(transferring, Transferring,
OnParam | DeclModifier | UserInaccessible | NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIStableToRemove,
157)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6727,6 +6727,7 @@ class ParamDecl : public VarDecl {
return true;
case Specifier::Consuming:
case Specifier::InOut:
case Specifier::Transferring:
return false;
}
llvm_unreachable("unhandled specifier");
Expand All @@ -6744,6 +6745,7 @@ class ParamDecl : public VarDecl {
case ParamSpecifier::LegacyShared:
return ValueOwnership::Shared;
case ParamSpecifier::Consuming:
case ParamSpecifier::Transferring:
case ParamSpecifier::LegacyOwned:
return ValueOwnership::Owned;
case ParamSpecifier::Default:
Expand Down
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,12 @@ WARNING(regionbasedisolation_transfer_yields_race_no_isolation, none,
WARNING(regionbasedisolation_transfer_yields_race_with_isolation, none,
"passing argument of non-sendable type %0 from %1 context to %2 context at this call site could yield a race with accesses later in this function",
(Type, ActorIsolation, ActorIsolation))
WARNING(regionbasedisolation_transfer_yields_race_transferring_parameter, none,
"transferred value of non-Sendable type %0 into transferring parameter; later accesses could result in races",
(Type))
WARNING(regionbasedisolation_transfer_yields_race_stronglytransferred_binding, none,
"binding of non-Sendable type %0 accessed after being transferred; later accesses could result in races",
(Type))
NOTE(regionbasedisolation_maybe_race, none,
"access here could race", ())
ERROR(regionbasedisolation_unknown_pattern, none,
Expand Down
37 changes: 28 additions & 9 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -2211,7 +2211,11 @@ enum class ParamSpecifier : uint8_t {
/// `__shared`, a legacy spelling of `borrowing`.
LegacyShared = 4,
/// `__owned`, a legacy spelling of `consuming`.
LegacyOwned = 5
LegacyOwned = 5,

/// `transferring`. Indicating the transfer of a value from one isolation
/// domain to another.
Transferring = 6,
};

/// Provide parameter type relevant flags, i.e. variadic, autoclosure, and
Expand All @@ -2228,7 +2232,8 @@ class ParameterTypeFlags {
Isolated = 1 << 7,
CompileTimeConst = 1 << 8,
ResultDependsOn = 1 << 9,
NumBits = 10
Transferring = 1 << 10,
NumBits = 11
};
OptionSet<ParameterFlags> value;
static_assert(NumBits <= 8*sizeof(OptionSet<ParameterFlags>), "overflowed");
Expand All @@ -2243,20 +2248,22 @@ class ParameterTypeFlags {

ParameterTypeFlags(bool variadic, bool autoclosure, bool nonEphemeral,
ParamSpecifier specifier, bool isolated, bool noDerivative,
bool compileTimeConst, bool hasResultDependsOn)
bool compileTimeConst, bool hasResultDependsOn,
bool isTransferring)
: value((variadic ? Variadic : 0) | (autoclosure ? AutoClosure : 0) |
(nonEphemeral ? NonEphemeral : 0) |
uint8_t(specifier) << SpecifierShift | (isolated ? Isolated : 0) |
(noDerivative ? NoDerivative : 0) |
(compileTimeConst ? CompileTimeConst : 0) |
(hasResultDependsOn ? ResultDependsOn : 0)) {}
(hasResultDependsOn ? ResultDependsOn : 0) |
(isTransferring ? Transferring : 0)) {}

/// Create one from what's present in the parameter type
inline static ParameterTypeFlags
fromParameterType(Type paramTy, bool isVariadic, bool isAutoClosure,
bool isNonEphemeral, ParamSpecifier ownership,
bool isolated, bool isNoDerivative, bool compileTimeConst,
bool hasResultDependsOn);
bool hasResultDependsOn, bool isTransferring);

bool isNone() const { return !value; }
bool isVariadic() const { return value.contains(Variadic); }
Expand All @@ -2269,6 +2276,7 @@ class ParameterTypeFlags {
bool isCompileTimeConst() const { return value.contains(CompileTimeConst); }
bool isNoDerivative() const { return value.contains(NoDerivative); }
bool hasResultDependsOn() const { return value.contains(ResultDependsOn); }
bool isTransferring() const { return value.contains(Transferring); }

/// Get the spelling of the parameter specifier used on the parameter.
ParamSpecifier getOwnershipSpecifier() const {
Expand Down Expand Up @@ -2331,6 +2339,12 @@ class ParameterTypeFlags {
: value - ParameterTypeFlags::NoDerivative);
}

ParameterTypeFlags withTransferring(bool withTransferring) const {
return ParameterTypeFlags(withTransferring
? value | ParameterTypeFlags::Transferring
: value - ParameterTypeFlags::Transferring);
}

bool operator ==(const ParameterTypeFlags &other) const {
return value.toRaw() == other.value.toRaw();
}
Expand Down Expand Up @@ -2424,7 +2438,8 @@ class YieldTypeFlags {
/*nonEphemeral*/ false, getOwnershipSpecifier(),
/*isolated*/ false, /*noDerivative*/ false,
/*compileTimeConst*/ false,
/*hasResultDependsOn*/ false);
/*hasResultDependsOn*/ false,
/*is transferring*/ false);
}

bool operator ==(const YieldTypeFlags &other) const {
Expand Down Expand Up @@ -4111,6 +4126,9 @@ class SILParameterInfo {
/// - If the function type is `@differentiable`, the function is
/// differentiable with respect to this parameter.
NotDifferentiable = 0x1,

/// Set if the given parameter is transferring.
Transferring = 0x2,
};

using Options = OptionSet<Flag>;
Expand Down Expand Up @@ -7627,7 +7645,7 @@ inline TupleTypeElt TupleTypeElt::getWithType(Type T) const {
inline ParameterTypeFlags ParameterTypeFlags::fromParameterType(
Type paramTy, bool isVariadic, bool isAutoClosure, bool isNonEphemeral,
ParamSpecifier ownership, bool isolated, bool isNoDerivative,
bool compileTimeConst, bool hasResultDependsOn) {
bool compileTimeConst, bool hasResultDependsOn, bool isTransferring) {
// FIXME(Remove InOut): The last caller that needs this is argument
// decomposition. Start by enabling the assertion there and fixing up those
// callers, then remove this, then remove
Expand All @@ -7637,8 +7655,9 @@ inline ParameterTypeFlags ParameterTypeFlags::fromParameterType(
ownership == ParamSpecifier::InOut);
ownership = ParamSpecifier::InOut;
}
return {isVariadic, isAutoClosure, isNonEphemeral, ownership,
isolated, isNoDerivative, compileTimeConst, hasResultDependsOn};
return {isVariadic, isAutoClosure, isNonEphemeral,
ownership, isolated, isNoDerivative,
compileTimeConst, hasResultDependsOn, isTransferring};
}

inline const Type *BoundGenericType::getTrailingObjectsPointer() const {
Expand Down
21 changes: 21 additions & 0 deletions include/swift/SIL/ApplySite.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,18 @@ class FullApplySite : public ApplySite {
getNumIndirectSILErrorResults());
}

MutableArrayRef<Operand> getOperandsWithoutIndirectResults() const {
return getArgumentOperands().slice(getNumIndirectSILResults() +
getNumIndirectSILErrorResults());
}

MutableArrayRef<Operand> getOperandsWithoutIndirectResultsOrSelf() const {
auto ops = getOperandsWithoutIndirectResults();
if (!hasSelfArgument())
return ops;
return ops.drop_back();
}

InoutArgumentRange getInoutArguments() const {
switch (getKind()) {
case FullApplySiteKind::ApplyInst:
Expand Down Expand Up @@ -789,6 +801,15 @@ class FullApplySite : public ApplySite {
}
}

SILParameterInfo getArgumentParameterInfo(const Operand &oper) const {
assert(!getArgumentConvention(oper).isIndirectOutParameter() &&
"Can only be applied to non-out parameters");

// The ParameterInfo is going to be the parameter in the caller.
unsigned calleeArgIndex = getCalleeArgIndex(oper);
return getSubstCalleeConv().getParamInfoForSILArg(calleeArgIndex);
}

static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); }

static bool classof(const SILInstruction *inst) {
Expand Down
4 changes: 4 additions & 0 deletions include/swift/SIL/SILArgument.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,10 @@ class SILFunctionArgument : public SILArgument {
sharedUInt32().SILFunctionArgument.hasResultDependsOn = flag;
}

bool isTransferring() const {
return getKnownParameterInfo().hasOption(SILParameterInfo::Transferring);
}

Lifetime getLifetime() const {
return getType()
.getLifetime(*getFunction())
Expand Down
4 changes: 4 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3009,6 +3009,10 @@ class ApplyInstBase<Impl, Base, true>
return getArguments().slice(getNumIndirectResults());
}

MutableArrayRef<Operand> getOperandsWithoutIndirectResults() const {
return getArgumentOperands().slice(getNumIndirectResults());
}

/// Returns all `@inout` and `@inout_aliasable` arguments passed to the
/// instruction.
InoutArgumentRange getInoutArguments() const {
Expand Down
9 changes: 9 additions & 0 deletions include/swift/SIL/SILValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -1655,6 +1655,15 @@ namespace llvm {
enum { NumLowBitsAvailable = swift::SILValue::NumLowBitsAvailable };
};

/// A SILValue can be checked if a value is present, so we can use it with
/// dyn_cast_or_null.
template <>
struct ValueIsPresent<swift::SILValue> {
using SILValue = swift::SILValue;
using UnwrappedType = SILValue;
static inline bool isPresent(const SILValue &t) { return bool(t); }
static inline decltype(auto) unwrapValue(SILValue &t) { return t; }
};
} // end namespace llvm

#endif
4 changes: 4 additions & 0 deletions include/swift/SILOptimizer/Analysis/RegionAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ class regionanalysisimpl::TrackableValue {

TrackableValueState getValueState() const { return valueState; }

/// Returns true if this TrackableValue is an alloc_stack from a transferring
/// parameter.
bool isTransferringParameter() const;

void print(llvm::raw_ostream &os) const {
os << "TrackableValue. State: ";
valueState.print(os);
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,10 @@ BridgedSpecifierTypeRepr BridgedSpecifierTypeRepr_createParsed(
return new (context)
OwnershipTypeRepr(baseType, ParamSpecifier::LegacyOwned, loc);
}
case BridgedAttributedTypeSpecifierTransferring: {
return new (context)
OwnershipTypeRepr(baseType, ParamSpecifier::Transferring, loc);
}
case BridgedAttributedTypeSpecifierConst: {
return new (context) CompileTimeConstTypeRepr(baseType, loc);
}
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2875,7 +2875,8 @@ getParameterFlagsForMangling(ParameterTypeFlags flags,
// `inout` should already be specified in the flags.
case ParamSpecifier::InOut:
return flags;


case ParamSpecifier::Transferring:
case ParamSpecifier::Consuming:
case ParamSpecifier::Borrowing:
// Only mangle the ownership if it diverges from the default.
Expand Down
20 changes: 19 additions & 1 deletion lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3906,7 +3906,15 @@ static bool usesFeatureExtractConstantsFromMembers(Decl *decl) {

static bool usesFeatureBitwiseCopyable(Decl *decl) { return false; }

static bool usesFeatureTransferringArgsAndResults(Decl *decl) { return false; }
static bool usesFeatureTransferringArgsAndResults(Decl *decl) {
if (auto *pd = dyn_cast<ParamDecl>(decl))
if (pd->getSpecifier() == ParamSpecifier::Transferring)
return true;

// TODO: Results.

return false;
}

static bool usesFeaturePreconcurrencyConformances(Decl *decl) {
auto usesPreconcurrencyConformance = [&](const InheritedTypes &inherited) {
Expand Down Expand Up @@ -4567,6 +4575,8 @@ static void printParameterFlags(ASTPrinter &printer,
printer.printAttrName("@autoclosure ");
if (!options.excludeAttrKind(TAK_noDerivative) && flags.isNoDerivative())
printer.printAttrName("@noDerivative ");
if (flags.isTransferring())
printer.printAttrName("@transferring ");

switch (flags.getOwnershipSpecifier()) {
case ParamSpecifier::Default:
Expand All @@ -4587,6 +4597,9 @@ static void printParameterFlags(ASTPrinter &printer,
case ParamSpecifier::LegacyOwned:
printer.printKeyword("__owned", options, " ");
break;
case ParamSpecifier::Transferring:
printer.printKeyword("transferring", options, " ");
break;
}

if (flags.isIsolated())
Expand Down Expand Up @@ -8068,6 +8081,11 @@ void SILParameterInfo::print(ASTPrinter &Printer,
Printer << "@noDerivative ";
}

if (options.contains(SILParameterInfo::Transferring)) {
options -= SILParameterInfo::Transferring;
Printer << "@transferring ";
}

// If we did not handle a case in Options, this code was not updated
// appropriately.
assert(!bool(options) && "Code not updated for introduced option");
Expand Down
6 changes: 5 additions & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7746,6 +7746,7 @@ void ParamDecl::setSpecifier(Specifier specifier) {
// `inout` and `consuming` parameters are locally mutable.
case ParamSpecifier::InOut:
case ParamSpecifier::Consuming:
case ParamSpecifier::Transferring:
introducer = VarDecl::Introducer::Var;
break;
}
Expand Down Expand Up @@ -7809,6 +7810,8 @@ StringRef ParamDecl::getSpecifierSpelling(ParamSpecifier specifier) {
return "__shared";
case ParamSpecifier::LegacyOwned:
return "__owned";
case ParamSpecifier::Transferring:
return "transferring";
}
llvm_unreachable("invalid ParamSpecifier");
}
Expand Down Expand Up @@ -8410,7 +8413,8 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const {
auto flags = ParameterTypeFlags::fromParameterType(
type, isVariadic(), isAutoClosure(), isNonEphemeral(), getSpecifier(),
isIsolated(), /*isNoDerivative*/ false, isCompileTimeConst(),
hasResultDependsOn());
hasResultDependsOn(),
getSpecifier() == ParamDecl::Specifier::Transferring /*is transferring*/);
return AnyFunctionType::Param(type, label, flags, internalLabel);
}

Expand Down
14 changes: 14 additions & 0 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5137,6 +5137,7 @@ ParserStatus Parser::ParsedTypeAttributeList::slowParse(Parser &P) {
Tok.isContextualKeyword("isolated") ||
Tok.isContextualKeyword("consuming") ||
Tok.isContextualKeyword("borrowing") ||
Tok.isContextualKeyword("transferring") ||
Tok.isContextualKeyword("_const") ||
Tok.isContextualKeyword("_resultDependsOn")))) {

Expand Down Expand Up @@ -5164,6 +5165,15 @@ ParserStatus Parser::ParsedTypeAttributeList::slowParse(Parser &P) {
continue;
}

// Perform an extra check for transferring. Since it is a specifier, we use
// the actual parsing logic below.
if (Tok.isContextualKeyword("transferring")) {
if (!P.Context.LangOpts.hasFeature(Feature::TransferringArgsAndResults)) {
P.diagnose(Tok, diag::requires_experimental_feature, "transferring",
false, getFeatureName(Feature::TransferringArgsAndResults));
}
}

if (SpecifierLoc.isValid()) {
P.diagnose(Tok, diag::parameter_specifier_repeated)
.fixItRemove(SpecifierLoc);
Expand All @@ -5177,6 +5187,8 @@ ParserStatus Parser::ParsedTypeAttributeList::slowParse(Parser &P) {
Specifier = ParamDecl::Specifier::LegacyOwned;
} else if (Tok.getRawText().equals("borrowing")) {
Specifier = ParamDecl::Specifier::Borrowing;
} else if (Tok.getRawText().equals("transferring")) {
Specifier = ParamDecl::Specifier::Transferring;
} else if (Tok.getRawText().equals("consuming")) {
Specifier = ParamDecl::Specifier::Consuming;
}
Expand Down Expand Up @@ -7539,6 +7551,8 @@ static ParserStatus parseAccessorIntroducer(Parser &P,
P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Consuming);
} else if (P.Tok.isContextualKeyword("borrowing")) {
P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Borrowing);
} else if (P.Tok.isContextualKeyword("transferring")) {
P.parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Transferring);
}
}

Expand Down
Loading