Skip to content

Commit e809460

Browse files
committed
[AST] Add @_nonEphemeral parameter attribute
This non-user-facing attribute is used to denote pointer parameters which do not accept pointers produced from temporary pointer conversions such as array-to-pointer, string-to-pointer, and in some cases inout-to-pointer.
1 parent ea4524c commit e809460

16 files changed

+108
-18
lines changed

include/swift/AST/Attr.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,10 @@ DECL_ATTR(_projectedValueProperty, ProjectedValueProperty,
496496
OnVar | UserInaccessible |
497497
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
498498
89)
499+
SIMPLE_DECL_ATTR(_nonEphemeral, NonEphemeral,
500+
OnParam | UserInaccessible |
501+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
502+
90)
499503

500504
SIMPLE_DECL_ATTR(IBSegueAction, IBSegueAction,
501505
OnFunc |

include/swift/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5320,6 +5320,11 @@ class ParamDecl : public VarDecl {
53205320
: flags - Flags::IsAutoClosure);
53215321
}
53225322

5323+
/// Does this parameter reject temporary pointer conversions?
5324+
bool isNonEphemeral() const {
5325+
return getAttrs().hasAttribute<NonEphemeralAttr>();
5326+
}
5327+
53235328
/// Remove the type of this varargs element designator, without the array
53245329
/// type wrapping it. A parameter like "Int..." will have formal parameter
53255330
/// type of "[Int]" and this returns "Int".

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,6 +2749,10 @@ ERROR(escaping_non_function_parameter,none,
27492749
NOTE(escaping_optional_type_argument, none,
27502750
"closure is already escaping in optional type argument", ())
27512751

2752+
// @_nonEphemeral attribute
2753+
ERROR(non_ephemeral_non_pointer_type,none,
2754+
"@_nonEphemeral attribute currently only applies to pointer types", ())
2755+
27522756
// NSManaged attribute
27532757
ERROR(attr_NSManaged_not_instance_member,none,
27542758
"@NSManaged only allowed on an instance property or method", ())

include/swift/AST/Types.h

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,13 +1761,14 @@ class TypeAliasType final
17611761
/// escaping.
17621762
class ParameterTypeFlags {
17631763
enum ParameterFlags : uint8_t {
1764-
None = 0,
1765-
Variadic = 1 << 0,
1766-
AutoClosure = 1 << 1,
1767-
OwnershipShift = 2,
1768-
Ownership = 7 << OwnershipShift,
1769-
1770-
NumBits = 5
1764+
None = 0,
1765+
Variadic = 1 << 0,
1766+
AutoClosure = 1 << 1,
1767+
NonEphemeral = 1 << 2,
1768+
OwnershipShift = 3,
1769+
Ownership = 7 << OwnershipShift,
1770+
1771+
NumBits = 6
17711772
};
17721773
OptionSet<ParameterFlags> value;
17731774
static_assert(NumBits < 8*sizeof(OptionSet<ParameterFlags>), "overflowed");
@@ -1780,19 +1781,21 @@ class ParameterTypeFlags {
17801781
return ParameterTypeFlags(OptionSet<ParameterFlags>(raw));
17811782
}
17821783

1783-
ParameterTypeFlags(bool variadic, bool autoclosure,
1784+
ParameterTypeFlags(bool variadic, bool autoclosure, bool nonEphemeral,
17841785
ValueOwnership ownership)
17851786
: value((variadic ? Variadic : 0) | (autoclosure ? AutoClosure : 0) |
1787+
(nonEphemeral ? NonEphemeral : 0) |
17861788
uint8_t(ownership) << OwnershipShift) {}
17871789

17881790
/// Create one from what's present in the parameter type
17891791
inline static ParameterTypeFlags
17901792
fromParameterType(Type paramTy, bool isVariadic, bool isAutoClosure,
1791-
ValueOwnership ownership);
1793+
bool isNonEphemeral, ValueOwnership ownership);
17921794

17931795
bool isNone() const { return !value; }
17941796
bool isVariadic() const { return value.contains(Variadic); }
17951797
bool isAutoClosure() const { return value.contains(AutoClosure); }
1798+
bool isNonEphemeral() const { return value.contains(NonEphemeral); }
17961799
bool isInOut() const { return getValueOwnership() == ValueOwnership::InOut; }
17971800
bool isShared() const { return getValueOwnership() == ValueOwnership::Shared;}
17981801
bool isOwned() const { return getValueOwnership() == ValueOwnership::Owned; }
@@ -1832,6 +1835,12 @@ class ParameterTypeFlags {
18321835
: value - ParameterTypeFlags::AutoClosure);
18331836
}
18341837

1838+
ParameterTypeFlags withNonEphemeral(bool isNonEphemeral) const {
1839+
return ParameterTypeFlags(isNonEphemeral
1840+
? value | ParameterTypeFlags::NonEphemeral
1841+
: value - ParameterTypeFlags::NonEphemeral);
1842+
}
1843+
18351844
bool operator ==(const ParameterTypeFlags &other) const {
18361845
return value.toRaw() == other.value.toRaw();
18371846
}
@@ -1898,6 +1907,7 @@ class YieldTypeFlags {
18981907
ParameterTypeFlags asParamFlags() const {
18991908
return ParameterTypeFlags(/*variadic*/ false,
19001909
/*autoclosure*/ false,
1910+
/*nonEphemeral*/ false,
19011911
getValueOwnership());
19021912
}
19031913

@@ -2767,6 +2777,9 @@ class AnyFunctionType : public TypeBase {
27672777
/// Whether the parameter is marked 'owned'
27682778
bool isOwned() const { return Flags.isOwned(); }
27692779

2780+
/// Whether the parameter is marked '@_nonEphemeral'
2781+
bool isNonEphemeral() const { return Flags.isNonEphemeral(); }
2782+
27702783
ValueOwnership getValueOwnership() const {
27712784
return Flags.getValueOwnership();
27722785
}
@@ -5562,7 +5575,7 @@ inline TupleTypeElt TupleTypeElt::getWithType(Type T) const {
55625575
/// Create one from what's present in the parameter decl and type
55635576
inline ParameterTypeFlags
55645577
ParameterTypeFlags::fromParameterType(Type paramTy, bool isVariadic,
5565-
bool isAutoClosure,
5578+
bool isAutoClosure, bool isNonEphemeral,
55665579
ValueOwnership ownership) {
55675580
// FIXME(Remove InOut): The last caller that needs this is argument
55685581
// decomposition. Start by enabling the assertion there and fixing up those
@@ -5573,7 +5586,7 @@ ParameterTypeFlags::fromParameterType(Type paramTy, bool isVariadic,
55735586
ownership == ValueOwnership::InOut);
55745587
ownership = ValueOwnership::InOut;
55755588
}
5576-
return {isVariadic, isAutoClosure, ownership};
5589+
return {isVariadic, isAutoClosure, isNonEphemeral, ownership};
55775590
}
55785591

55795592
inline const Type *BoundGenericType::getTrailingObjectsPointer() const {

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2922,7 +2922,7 @@ void AnyFunctionType::decomposeInput(
29222922
default:
29232923
result.emplace_back(type->getInOutObjectType(), Identifier(),
29242924
ParameterTypeFlags::fromParameterType(
2925-
type, false, false, ValueOwnership::Default));
2925+
type, false, false, false, ValueOwnership::Default));
29262926
return;
29272927
}
29282928
}

lib/AST/ASTDumper.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,9 @@ namespace {
988988
if (P->isAutoClosure())
989989
OS << " autoclosure";
990990

991+
if (P->isNonEphemeral())
992+
OS << " nonEphemeral";
993+
991994
if (P->getDefaultArgumentKind() != DefaultArgumentKind::None) {
992995
printField("default_arg",
993996
getDefaultArgumentKindString(P->getDefaultArgumentKind()));
@@ -3306,6 +3309,7 @@ namespace {
33063309
void dumpParameterFlags(ParameterTypeFlags paramFlags) {
33073310
printFlag(paramFlags.isVariadic(), "vararg");
33083311
printFlag(paramFlags.isAutoClosure(), "autoclosure");
3312+
printFlag(paramFlags.isNonEphemeral(), "nonEphemeral");
33093313
switch (paramFlags.getValueOwnership()) {
33103314
case ValueOwnership::Default: break;
33113315
case ValueOwnership::Owned: printFlag("owned"); break;

lib/AST/Decl.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2535,7 +2535,9 @@ static Type mapSignatureFunctionType(ASTContext &ctx, Type type,
25352535
SmallVector<AnyFunctionType::Param, 4> newParams;
25362536
for (const auto &param : funcTy->getParams()) {
25372537
auto newParamType = mapSignatureParamType(ctx, param.getPlainType());
2538-
ParameterTypeFlags newFlags = param.getParameterFlags();
2538+
2539+
// Don't allow overloading by @_nonEphemeral.
2540+
auto newFlags = param.getParameterFlags().withNonEphemeral(false);
25392541

25402542
// For the 'self' of a method, strip off 'inout'.
25412543
if (isMethod) {
@@ -5879,6 +5881,7 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const {
58795881
auto flags = ParameterTypeFlags::fromParameterType(type,
58805882
isVariadic(),
58815883
isAutoClosure(),
5884+
isNonEphemeral(),
58825885
getValueOwnership());
58835886
return AnyFunctionType::Param(type, label, flags);
58845887
}

lib/Sema/ConstraintLocator.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,8 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
306306
auto argElt = elt.castTo<LocatorPathElt::ApplyArgToParam>();
307307
out << "comparing call argument #" << llvm::utostr(argElt.getArgIdx())
308308
<< " to parameter #" << llvm::utostr(argElt.getParamIdx());
309+
if (argElt.getParameterFlags().isNonEphemeral())
310+
out << " (non-ephemeral)";
309311
break;
310312
}
311313
case ClosureResult:

lib/Sema/TypeCheckAttr.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
237237
void visitFunctionBuilderAttr(FunctionBuilderAttr *attr);
238238

239239
void visitImplementationOnlyAttr(ImplementationOnlyAttr *attr);
240+
void visitNonEphemeralAttr(NonEphemeralAttr *attr);
240241
};
241242
} // end anonymous namespace
242243

@@ -2676,6 +2677,25 @@ AttributeChecker::visitImplementationOnlyAttr(ImplementationOnlyAttr *attr) {
26762677
// it won't necessarily be able to say why.
26772678
}
26782679

2680+
void AttributeChecker::visitNonEphemeralAttr(NonEphemeralAttr *attr) {
2681+
auto *param = cast<ParamDecl>(D);
2682+
auto type = param->getInterfaceType()->lookThroughSingleOptionalType();
2683+
2684+
// Can only be applied to Unsafe[...]Pointer types
2685+
if (type->getAnyPointerElementType())
2686+
return;
2687+
2688+
// ... or the protocol Self type.
2689+
auto *outerDC = param->getDeclContext()->getParent();
2690+
if (outerDC->getSelfProtocolDecl() &&
2691+
type->isEqual(outerDC->getProtocolSelfType())) {
2692+
return;
2693+
}
2694+
2695+
TC.diagnose(attr->getLocation(), diag::non_ephemeral_non_pointer_type);
2696+
attr->setInvalid();
2697+
}
2698+
26792699
void TypeChecker::checkParameterAttributes(ParameterList *params) {
26802700
for (auto param: *params) {
26812701
checkDeclAttributes(param);

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,7 @@ namespace {
12931293
UNINTERESTING_ATTR(LLDBDebuggerFunction)
12941294
UNINTERESTING_ATTR(Mutating)
12951295
UNINTERESTING_ATTR(NonMutating)
1296+
UNINTERESTING_ATTR(NonEphemeral)
12961297
UNINTERESTING_ATTR(NonObjC)
12971298
UNINTERESTING_ATTR(NonOverride)
12981299
UNINTERESTING_ATTR(NSApplicationMain)

lib/Sema/TypeCheckType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2415,7 +2415,7 @@ bool TypeResolver::resolveASTFunctionTypeParams(
24152415
break;
24162416
}
24172417
auto paramFlags = ParameterTypeFlags::fromParameterType(
2418-
ty, variadic, autoclosure, ownership);
2418+
ty, variadic, autoclosure, /*isNonEphemeral*/ false, ownership);
24192419
elements.emplace_back(ty, Identifier(), paramFlags);
24202420
}
24212421

lib/Serialization/Deserialization.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4615,10 +4615,11 @@ class swift::TypeDeserializer {
46154615

46164616
IdentifierID labelID;
46174617
TypeID typeID;
4618-
bool isVariadic, isAutoClosure;
4618+
bool isVariadic, isAutoClosure, isNonEphemeral;
46194619
unsigned rawOwnership;
46204620
decls_block::FunctionParamLayout::readRecord(scratch, labelID, typeID,
46214621
isVariadic, isAutoClosure,
4622+
isNonEphemeral,
46224623
rawOwnership);
46234624

46244625
auto ownership =
@@ -4633,7 +4634,7 @@ class swift::TypeDeserializer {
46334634
params.emplace_back(paramTy.get(),
46344635
MF.getIdentifier(labelID),
46354636
ParameterTypeFlags(isVariadic, isAutoClosure,
4636-
*ownership));
4637+
isNonEphemeral, *ownership));
46374638
}
46384639

46394640
if (!isGeneric) {

lib/Serialization/ModuleFormat.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 519; // SIL function availability
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 520; // @_nonEphemeral
5656

5757
/// A standard hash seed used for all string hashes in a serialized module.
5858
///
@@ -887,6 +887,7 @@ namespace decls_block {
887887
TypeIDField, // type
888888
BCFixed<1>, // vararg?
889889
BCFixed<1>, // autoclosure?
890+
BCFixed<1>, // non-ephemeral?
890891
ValueOwnershipField // inout, shared or owned?
891892
>;
892893

lib/Serialization/Serialization.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3863,7 +3863,8 @@ class Serializer::TypeSerializer : public TypeVisitor<TypeSerializer> {
38633863
S.Out, S.ScratchRecord, abbrCode,
38643864
S.addDeclBaseNameRef(param.getLabel()),
38653865
S.addTypeRef(param.getPlainType()), paramFlags.isVariadic(),
3866-
paramFlags.isAutoClosure(), rawOwnership);
3866+
paramFlags.isAutoClosure(), paramFlags.isNonEphemeral(),
3867+
rawOwnership);
38673868
}
38683869
}
38693870

test/attr/attributes.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,30 @@ func unownedOptionals(x: C) {
283283
_ = y
284284
_ = y2
285285
}
286+
287+
// @_nonEphemeral attribute
288+
struct S1<T> {
289+
func foo(@_nonEphemeral _ x: String) {} // expected-error {{@_nonEphemeral attribute currently only applies to pointer types}}
290+
func bar(@_nonEphemeral _ x: T) {} // expected-error {{@_nonEphemeral attribute currently only applies to pointer types}}
291+
292+
func baz<U>(@_nonEphemeral _ x: U) {} // expected-error {{@_nonEphemeral attribute currently only applies to pointer types}}
293+
294+
func qux(@_nonEphemeral _ x: UnsafeMutableRawPointer) {}
295+
func quux(@_nonEphemeral _ x: UnsafeMutablePointer<Int>?) {}
296+
}
297+
298+
@_nonEphemeral struct S2 {} // expected-error {{@_nonEphemeral may only be used on 'parameter' declarations}}
299+
300+
protocol P {}
301+
extension P {
302+
// Allow @_nonEphemeral on the protocol Self type, as the protocol could be adopted by a pointer type.
303+
func foo(@_nonEphemeral _ x: Self) {}
304+
func bar(@_nonEphemeral _ x: Self?) {}
305+
}
306+
307+
enum E1 {
308+
case str(@_nonEphemeral _: String) // expected-error {{expected parameter name followed by ':'}}
309+
case ptr(@_nonEphemeral _: UnsafeMutableRawPointer) // expected-error {{expected parameter name followed by ':'}}
310+
311+
func foo() -> @_nonEphemeral UnsafeMutableRawPointer? { return nil } // expected-error {{attribute can only be applied to declarations, not types}}
312+
}

test/decl/overload.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,10 @@ struct Escaping {
368368
func autoclosure(f: () -> Int) { }
369369
func autoclosure(f: @autoclosure () -> Int) { }
370370

371+
// @_nonEphemeral
372+
func nonEphemeral(x: UnsafeMutableRawPointer) {} // expected-note {{'nonEphemeral(x:)' previously declared here}}
373+
func nonEphemeral(@_nonEphemeral x: UnsafeMutableRawPointer) {} // expected-error {{invalid redeclaration of 'nonEphemeral(x:)'}}
374+
371375
// inout
372376
func inout2(x: Int) { }
373377
func inout2(x: inout Int) { }

0 commit comments

Comments
 (0)