Skip to content

Commit 7dd1c05

Browse files
committed
wip
1 parent c27e2c7 commit 7dd1c05

25 files changed

+939
-40
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,106 @@ Fully bypasses access control, allowing access to private declarations
832832
in the imported module. The imported module needs to be compiled with
833833
`-Xfrontend -enable-private-imports` for this to work.
834834

835+
## `@_rawLayout(...)`
836+
837+
Specifies the declared type consists of raw storage. The type must be
838+
noncopyable, and have no other declared stored properties.
839+
Raw storage is left almost entirely unmanaged by the language, and so
840+
can be used as storage for data structures with nonstandard access patterns,
841+
including atomics, many kinds of locks, to replicate the behavior of things
842+
like C++'s `mutable` fields or Rust's `Cell<T>` type which allow for mutation
843+
in typically immutable contexts, or to provide inline storage for data
844+
structures that may be conditionally initialized, such as a "small vector".
845+
846+
In particular, programmers can safely make the following assumptions about
847+
the memory of the annotated type:
848+
849+
- A value has a **stable address** until it is either consumed or moved.
850+
No value can ever be moved while it is being borrowed or mutated, so
851+
the address of `self` within a `borrowing` or `mutating` method, or more
852+
generally of a `borrowing` or `inout` parameter to any function, cannot
853+
change during the function body.
854+
Values that appear in a global variable or class stored property can
855+
never be moved, and can only be consumed by the deallocation of the containing
856+
object instance, so effectively has a stable address for their entire lifetime.
857+
- A value's memory **may be read and mutated at any time** independent of
858+
formal accesses. In particular, pointers into the storage may be "escaped"
859+
outside of scopes where the address is statically guaranteed to be stable
860+
and may be used freely for as long as the storage dynamically isn't consumed
861+
or moved. It becomes the programmer's responsibility in this case to ensure
862+
that reads and writes to the storage do not race across threads, or overlap
863+
within the same thread, and that the pointer is not used after the value is
864+
moved or consumed.
865+
- When the value is moved, a bitwise copy of its memory is performed to the
866+
new address of the value in its new owner. As currently implemented, raw
867+
storage types are not suitable for storing values which are not
868+
bitwise-movable, such as nontrivial C++ types or Objective-C weak references.
869+
870+
Using the `@_rawLayout` attribute will suppress the annotated type from
871+
being implicitly `Sendable`. If the type is safe to access across threads, it
872+
may be marked `@unchecked Sendable`. This generally means that any
873+
mutations must be done atomically or with a lock guard, and if the storage
874+
is ever mutated, then any reads of potentially-mutated state within the storage
875+
must also be atomic or lock-guarded, because the storage may be accessed
876+
simultaneously by multiple threads.
877+
878+
A non-Sendable type's memory will be confined to accesses from a single thread
879+
or task; however, since most mutating operations in Swift still expect
880+
exclusivity while executing, a programmer must ensure that overlapping
881+
mutations cannot occur from aliasing, recursion, reentrancy, signal handlers, or
882+
other potential sources of overlapping access within the same thread.
883+
884+
The parameters to the attribute specify the layout of the type. The following
885+
forms are currently accepted:
886+
887+
- `@_rawLayout(size: N, alignment: M)` specifies the type's size and alignment
888+
in bytes.
889+
- `@_rawLayout(like: T)` specifies the type's size and alignment should be
890+
equal to the type `T`'s.
891+
- `@_rawLayout(likeArrayOf: T, count: N)` specifies the type's size should be
892+
`MemoryLayout<T>.stride * N` and alignment should match `T`'s, like an
893+
array of N contiguous elements of `T` in memory.
894+
895+
A notable difference between `@_rawLayout(like: T)` and
896+
`@_rawLayout(likeArrayOf: T, count: 1)` is that the latter will pad out the
897+
size of the raw storage to include the full stride of the single element.
898+
This ensures that the buffer can be safely used with bulk array operations
899+
despite containing only a single element. `@_rawLayout(like: T)` by contrast
900+
will exactly match the size and stride of the original type `T`, allowing for
901+
other values to be stored in the tail padding when the raw layout type appears
902+
in a larger aggregate.
903+
904+
```
905+
// struct Weird has size 5, stride 8, alignment 4
906+
struct Weird {
907+
var x: Int32
908+
var y: Int8
909+
}
910+
911+
// struct LikeWeird has size 5, stride 8, alignment 4
912+
@_rawLayout(like: Weird)
913+
struct LikeWeird {
914+
var x: Int32
915+
var y: Int8
916+
}
917+
918+
// struct LikeWeirdSingleArray has **size 8**, stride 8, alignment 4
919+
@_rawLayout(likeArrayOf: Weird, count: 1)
920+
struct LikeWeirdSingleArray {
921+
var x: Int32
922+
var y: Int8
923+
}
924+
```
925+
926+
Although the `like:` and `likeArrayOf:count:` forms will produce raw storage
927+
with the size and alignment of another type, the memory is **not** implicitly
928+
*bound* to that type, as *bound* is defined by the `UnsafePointer` and
929+
`UnsafeMutablePointer`. The memory can be accessed as raw memory
930+
if it is never explicitly bound using a typed pointer method like
931+
`withMemoryRebound(to:)` or `bindMemory(to:)`. If the raw memory is bound, it
932+
must only be used with compatible typed memory accesses for as long as the
933+
binding is active.
934+
835935
## `@_section("section_name")`
836936

837937
Places a global variable or a top-level function into a section of the object

include/swift/AST/Attr.h

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2485,6 +2485,107 @@ class MacroRoleAttr final
24852485
}
24862486
};
24872487

2488+
/// Specifies the raw storage used by a type.
2489+
class RawLayoutAttr final : public DeclAttribute {
2490+
/// The element type to share size and alignment with, if any.
2491+
TypeRepr *LikeType;
2492+
/// The number of elements in an array to share stride and alignment with,
2493+
/// or zero if no such size was specified. If `LikeType` is null, this is
2494+
/// the size in bytes of the raw storage.
2495+
unsigned SizeOrCount;
2496+
/// If `LikeType` is null, the alignment in bytes to use for the raw storage.
2497+
unsigned Alignment;
2498+
/// The resolved like type.
2499+
Type ResolvedLikeType = Type();
2500+
2501+
public:
2502+
/// Construct a `@_rawLayout(like: T)` attribute.
2503+
RawLayoutAttr(TypeRepr *LikeType,
2504+
SourceLoc AtLoc,
2505+
SourceRange Range)
2506+
: DeclAttribute(DAK_RawLayout, AtLoc, Range, /*implicit*/false),
2507+
LikeType(LikeType), SizeOrCount(0), Alignment(~0u)
2508+
{}
2509+
2510+
/// Construct a `@_rawLayout(likeArrayOf: T, count: N)` attribute.
2511+
RawLayoutAttr(TypeRepr *LikeType, unsigned Count,
2512+
SourceLoc AtLoc,
2513+
SourceRange Range)
2514+
: DeclAttribute(DAK_RawLayout, AtLoc, Range, /*implicit*/false),
2515+
LikeType(LikeType), SizeOrCount(Count), Alignment(0)
2516+
{}
2517+
2518+
/// Construct a `@_rawLayout(size: N, alignment: M)` attribute.
2519+
RawLayoutAttr(unsigned Size, unsigned Alignment,
2520+
SourceLoc AtLoc,
2521+
SourceRange Range)
2522+
: DeclAttribute(DAK_RawLayout, AtLoc, Range, /*implicit*/false),
2523+
LikeType(nullptr), SizeOrCount(Size), Alignment(Alignment)
2524+
{}
2525+
2526+
/// Return the type whose single-element layout the attribute type should get
2527+
/// its layout from. Returns null if the attribute specifies an array or manual
2528+
/// layout.
2529+
TypeRepr *getScalarLikeType() const {
2530+
if (!LikeType)
2531+
return nullptr;
2532+
if (Alignment != ~0u)
2533+
return nullptr;
2534+
return LikeType;
2535+
}
2536+
2537+
/// Return the type whose array layout the attribute type should get its
2538+
/// layout from, along with the size of that array. Returns None if the
2539+
/// attribute specifies scalar or manual layout.
2540+
llvm::Optional<std::pair<TypeRepr *, unsigned>> getArrayLikeTypeAndCount() const {
2541+
if (!LikeType)
2542+
return llvm::None;
2543+
if (Alignment == ~0u)
2544+
return llvm::None;
2545+
return std::make_pair(LikeType, SizeOrCount);
2546+
}
2547+
2548+
/// Return the size and alignment of the attributed type. Returns
2549+
/// None if the attribute specifies layout like some other type.
2550+
llvm::Optional<std::pair<unsigned, unsigned>> getSizeAndAlignment() const {
2551+
if (LikeType)
2552+
return llvm::None;
2553+
return std::make_pair(SizeOrCount, Alignment);
2554+
}
2555+
2556+
/// Set the resolved type.
2557+
void setResolvedLikeType(Type ty) {
2558+
assert(LikeType && "doesn't have a like type");
2559+
ResolvedLikeType = ty;
2560+
}
2561+
2562+
/// Return the type whose single-element layout the attribute type should get
2563+
/// its layout from. Returns None if the attribute specifies an array or manual
2564+
/// layout.
2565+
llvm::Optional<Type> getResolvedScalarLikeType() const {
2566+
if (!LikeType)
2567+
return llvm::None;
2568+
if (Alignment != ~0u)
2569+
return llvm::None;
2570+
return ResolvedLikeType;
2571+
}
2572+
2573+
/// Return the type whose array layout the attribute type should get its
2574+
/// layout from, along with the size of that array. Returns None if the
2575+
/// attribute specifies scalar or manual layout.
2576+
llvm::Optional<std::pair<Type, unsigned>> getResolvedArrayLikeTypeAndCount() const {
2577+
if (!LikeType)
2578+
return llvm::None;
2579+
if (Alignment == ~0u)
2580+
return llvm::None;
2581+
return std::make_pair(ResolvedLikeType, SizeOrCount);
2582+
}
2583+
2584+
static bool classof(const DeclAttribute *DA) {
2585+
return DA->getKind() == DAK_RawLayout;
2586+
}
2587+
};
2588+
24882589
/// Predicate used to filter MatchingAttributeRange.
24892590
template <typename ATTR, bool AllowInvalid> struct ToAttributeKind {
24902591
ToAttributeKind() {}

include/swift/AST/DiagnosticsParse.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,6 +1846,17 @@ ERROR(documentation_attr_metadata_expected_text,none,
18461846
ERROR(documentation_attr_duplicate_metadata,none,
18471847
"cannot give more than one metadata argument to the same item", ())
18481848

1849+
ERROR(attr_rawlayout_expected_label,none,
1850+
"expected %0 argument to @_rawLayout attribute", (StringRef))
1851+
ERROR(attr_rawlayout_expected_integer_size,none,
1852+
"expected integer literal size in @_rawLayout attribute", ())
1853+
ERROR(attr_rawlayout_expected_integer_alignment,none,
1854+
"expected integer literal alignment in @_rawLayout attribute", ())
1855+
ERROR(attr_rawlayout_expected_integer_count,none,
1856+
"expected integer literal count in @_rawLayout attribute", ())
1857+
ERROR(attr_rawlayout_expected_params,none,
1858+
"expected %1 argument after %0 argument in @_rawLayout attribute", (StringRef, StringRef))
1859+
18491860
//------------------------------------------------------------------------------
18501861
// MARK: Generics parsing diagnostics
18511862
//------------------------------------------------------------------------------

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3811,6 +3811,15 @@ ERROR(attr_MainType_without_main,none,
38113811
#undef SELECT_APPLICATION_MAIN
38123812
#undef SELECT_APPLICATION_DELEGATE
38133813

3814+
ERROR(attr_rawlayout_experimental,none,
3815+
"the @_rawLayout attribute is experimental", ())
3816+
ERROR(attr_rawlayout_cannot_be_copyable,none,
3817+
"type with @_rawLayout cannot be copied and must be declared ~Copyable", ())
3818+
ERROR(attr_rawlayout_cannot_have_stored_properties,none,
3819+
"type with @_rawLayout cannot have stored properties", ())
3820+
ERROR(attr_rawlayout_cannot_have_alignment_attr,none,
3821+
"type with @_rawLayout cannot also have an @_alignment attribute", ())
3822+
38143823
// lazy
38153824
ERROR(lazy_not_on_let,none,
38163825
"'lazy' cannot be used on a let", ())

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ EXPERIMENTAL_FEATURE(DeferredSendableChecking, false)
224224
/// "playground transform" is enabled.
225225
EXPERIMENTAL_FEATURE(PlaygroundExtendedCallbacks, true)
226226

227+
/// Enable the `@_rawLayout` attribute.
228+
EXPERIMENTAL_FEATURE(RawLayout, true)
229+
227230
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
228231
#undef EXPERIMENTAL_FEATURE
229232
#undef UPCOMING_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3439,6 +3439,10 @@ static bool usesFeatureBuiltinModule(Decl *decl) {
34393439
return false;
34403440
}
34413441

3442+
static bool usesFeatureRawLayout(Decl *decl) {
3443+
return decl->getAttrs().hasAttribute<RawLayoutAttr>();
3444+
}
3445+
34423446
static bool hasParameterPacks(Decl *decl) {
34433447
if (auto genericContext = decl->getAsGenericContext()) {
34443448
auto sig = genericContext->getGenericSignature();

lib/AST/Attr.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,28 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
14621462
Printer << ")";
14631463
break;
14641464
}
1465+
1466+
case DAK_RawLayout: {
1467+
auto *attr = cast<RawLayoutAttr>(this);
1468+
Printer.printAttrName("@_rawLayout");
1469+
Printer << "(";
1470+
1471+
if (auto sizeAndAlign = attr->getSizeAndAlignment()) {
1472+
Printer << "size: " << sizeAndAlign->first
1473+
<< ", alignment: " << sizeAndAlign->second;
1474+
} else if (auto type = attr->getScalarLikeType()) {
1475+
Printer << "like: ";
1476+
type->print(Printer, Options);
1477+
} else if (auto array = attr->getArrayLikeTypeAndCount()) {
1478+
Printer << "likeArrayOf: ";
1479+
array->first->print(Printer, Options);
1480+
Printer << ", count: " << array->second;
1481+
} else {
1482+
llvm_unreachable("unhandled @_rawLayout form");
1483+
}
1484+
Printer << ")";
1485+
break;
1486+
}
14651487

14661488
case DAK_Count:
14671489
llvm_unreachable("exceed declaration attribute kinds");
@@ -1653,6 +1675,8 @@ StringRef DeclAttribute::getAttrName() const {
16531675
case MacroSyntax::Attached:
16541676
return "attached";
16551677
}
1678+
case DAK_RawLayout:
1679+
return "_rawLayout";
16561680
}
16571681
llvm_unreachable("bad DeclAttrKind");
16581682
}

lib/IRGen/GenType.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2831,9 +2831,10 @@ SILType irgen::getSingletonAggregateFieldType(IRGenModule &IGM, SILType t,
28312831
|| structDecl->hasClangNode())
28322832
return SILType();
28332833

2834-
// A single-field struct with custom alignment has different layout from its
2834+
// A single-field struct with custom layout has different layout from its
28352835
// field.
2836-
if (structDecl->getAttrs().hasAttribute<AlignmentAttr>())
2836+
if (structDecl->getAttrs().hasAttribute<AlignmentAttr>()
2837+
|| structDecl->getAttrs().hasAttribute<RawLayoutAttr>())
28372838
return SILType();
28382839

28392840
// If there's only one stored property, we have the layout of its field.

0 commit comments

Comments
 (0)