Skip to content

Commit a9da08c

Browse files
committed
Add option for raw layout to move as its like type
1 parent 231f5b5 commit a9da08c

File tree

16 files changed

+240
-15
lines changed

16 files changed

+240
-15
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -955,12 +955,14 @@ the memory of the annotated type:
955955
threads, writes don't overlap with reads or writes coming from the same
956956
thread, and that the pointer is not used after the value is moved or
957957
consumed.
958-
- When the value is moved, a bitwise copy of its memory is performed to the new
959-
address of the value in its new owner. As currently implemented, raw storage
960-
types are not suitable for storing values which are not bitwise-movable, such
961-
as nontrivial C++ types, Objective-C weak references, and data structures
962-
such as `pthread_mutex_t` which are implemented in C as always requiring a
963-
fixed address.
958+
- By default, when the value is moved a bitwise copy of its memory is performed
959+
to the new address of the value in its new owner. This makes it unsuitable to
960+
store not bitwise-movable types such as nontrivial C++ types, Objective-C weak
961+
references, and data structures such as `pthread_mutex_t` which are
962+
implemented in C as always requiring a fixed address. However, you can provide
963+
`movesAsLike` to the `like:` version of this attribute to enforce that moving
964+
the value will defer its move semantics to the type it's like. This makes it
965+
suitable for storing such values that are not bitwise-movable.
964966

965967
Using the `@_rawLayout` attribute will suppress the annotated type from
966968
being implicitly `Sendable`. If the type is safe to access across threads, it
@@ -987,6 +989,11 @@ forms are currently accepted:
987989
- `@_rawLayout(likeArrayOf: T, count: N)` specifies the type's size should be
988990
`MemoryLayout<T>.stride * N` and alignment should match `T`'s, like an
989991
array of N contiguous elements of `T` in memory.
992+
- `@_rawLayout(like: T, movesAsLike)` specifies the type's size and alignment
993+
should be equal to the type `T`'s. It also guarantees that moving a value of
994+
this raw layout type will have the same move semantics as the type it's like.
995+
This is important for things like ObjC weak references and non-trivial move
996+
constructors in C++.
990997

991998
A notable difference between `@_rawLayout(like: T)` and
992999
`@_rawLayout(likeArrayOf: T, count: 1)` is that the latter will pad out the

include/swift/AST/Attr.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,17 +2536,21 @@ class RawLayoutAttr final : public DeclAttribute {
25362536
unsigned SizeOrCount;
25372537
/// If `LikeType` is null, the alignment in bytes to use for the raw storage.
25382538
unsigned Alignment;
2539+
/// If a value of this raw layout type should move like its `LikeType`.
2540+
bool MovesAsLike = false;
25392541
/// The resolved like type.
25402542
mutable Type CachedResolvedLikeType = Type();
25412543

25422544
friend class ResolveRawLayoutLikeTypeRequest;
25432545

25442546
public:
25452547
/// Construct a `@_rawLayout(like: T)` attribute.
2546-
RawLayoutAttr(TypeRepr *LikeType, SourceLoc AtLoc, SourceRange Range)
2548+
RawLayoutAttr(TypeRepr *LikeType, bool movesAsLike, SourceLoc AtLoc,
2549+
SourceRange Range)
25472550
: DeclAttribute(DeclAttrKind::RawLayout, AtLoc, Range,
25482551
/*implicit*/ false),
2549-
LikeType(LikeType), SizeOrCount(0), Alignment(~0u) {}
2552+
LikeType(LikeType), SizeOrCount(0), Alignment(~0u),
2553+
MovesAsLike(movesAsLike) {}
25502554

25512555
/// Construct a `@_rawLayout(likeArrayOf: T, count: N)` attribute.
25522556
RawLayoutAttr(TypeRepr *LikeType, unsigned Count, SourceLoc AtLoc,
@@ -2618,6 +2622,11 @@ class RawLayoutAttr final : public DeclAttribute {
26182622
return std::make_pair(getResolvedLikeType(sd), SizeOrCount);
26192623
}
26202624

2625+
/// Whether a value of this raw layout should move like its `LikeType`.
2626+
bool shouldMoveAsLikeType() const {
2627+
return MovesAsLike;
2628+
}
2629+
26212630
static bool classof(const DeclAttribute *DA) {
26222631
return DA->getKind() == DeclAttrKind::RawLayout;
26232632
}

include/swift/SIL/SILType.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,17 @@ class SILType {
891891
/// Returns true if this SILType is a differentiable type.
892892
bool isDifferentiable(SILModule &M) const;
893893

894+
/// Returns the @_rawLayout attribute on this type if it has one.
895+
RawLayoutAttr *getRawLayout() const {
896+
auto sd = getStructOrBoundGenericStruct();
897+
898+
if (!sd) {
899+
return nullptr;
900+
}
901+
902+
return sd->getAttrs().getAttribute<RawLayoutAttr>();
903+
}
904+
894905
/// If this is a SILBoxType, return getSILBoxFieldType(). Otherwise, return
895906
/// SILType().
896907
///

lib/IRGen/GenRecord.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,25 @@ class RecordTypeInfoImpl : public Base,
202202
return emitAssignWithTakeCall(IGF, T, dest, src);
203203
}
204204

205+
if (auto rawLayout = T.getRawLayout()) {
206+
// Because we have a rawlayout attribute, we know this has to be a struct.
207+
auto structDecl = T.getStructOrBoundGenericStruct();
208+
209+
if (auto likeType = rawLayout->getResolvedScalarLikeType(structDecl)) {
210+
if (rawLayout->shouldMoveAsLikeType()) {
211+
auto astT = T.getASTType();
212+
auto subs = astT->getContextSubstitutionMap(IGF.IGM.getSwiftModule(),
213+
structDecl);
214+
auto loweredLikeType = IGF.IGM.getLoweredType(likeType->subst(subs));
215+
auto &likeTypeInfo = IGF.IGM.getTypeInfo(loweredLikeType);
216+
217+
likeTypeInfo.assignWithTake(IGF, dest, src, loweredLikeType,
218+
isOutlined);
219+
return;
220+
}
221+
}
222+
}
223+
205224
if (isOutlined || T.hasParameterizedExistential()) {
206225
auto offsets = asImpl().getNonFixedOffsets(IGF, T);
207226
for (auto &field : getFields()) {
@@ -261,7 +280,22 @@ class RecordTypeInfoImpl : public Base,
261280
} else if (!AreFieldsABIAccessible) {
262281
// If the fields are not ABI-accessible, use the value witness table.
263282
return emitInitializeWithTakeCall(IGF, T, dest, src);
264-
283+
} else if (auto rawLayout = T.getRawLayout()) {
284+
// Because we have a rawlayout attribute, we know this has to be a struct.
285+
auto structDecl = T.getStructOrBoundGenericStruct();
286+
287+
if (auto likeType = rawLayout->getResolvedScalarLikeType(structDecl)) {
288+
if (rawLayout->shouldMoveAsLikeType()) {
289+
auto astT = T.getASTType();
290+
auto subs = astT->getContextSubstitutionMap(IGF.IGM.getSwiftModule(),
291+
structDecl);
292+
auto loweredLikeType = IGF.IGM.getLoweredType(likeType->subst(subs));
293+
auto &likeTypeInfo = IGF.IGM.getTypeInfo(loweredLikeType);
294+
295+
likeTypeInfo.initializeWithTake(IGF, dest, src, loweredLikeType,
296+
isOutlined, zeroizeIfSensitive);
297+
}
298+
}
265299
} else if (isOutlined || T.hasParameterizedExistential()) {
266300
auto offsets = asImpl().getNonFixedOffsets(IGF, T);
267301
for (auto &field : getFields()) {

lib/IRGen/StructLayout.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,26 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
125125
MinimumAlign = likeFixedType->getFixedAlignment();
126126
IsFixedLayout = true;
127127
IsKnownAlwaysFixedSize = IsFixedSize;
128+
129+
// @_rawLayout(like: T) has an optional `movesAsLike` which enforces that
130+
// a value of this raw layout type should have the same move semantics
131+
// as the like its like.
132+
if (rawLayout->shouldMoveAsLikeType()) {
133+
IsKnownTriviallyDestroyable = likeFixedType->isTriviallyDestroyable(ResilienceExpansion::Maximal);
134+
IsKnownBitwiseTakable = likeFixedType->isBitwiseTakable(ResilienceExpansion::Maximal);
135+
}
128136
} else {
129137
MinimumSize = Size(0);
130138
MinimumAlign = Alignment(1);
131139
IsFixedLayout = false;
132140
IsKnownAlwaysFixedSize = IsNotFixedSize;
141+
142+
// We don't know our like type, so assume we're not known to be bitwise
143+
// takable.
144+
if (rawLayout->shouldMoveAsLikeType()) {
145+
IsKnownTriviallyDestroyable = IsNotTriviallyDestroyable;
146+
IsKnownBitwiseTakable = IsNotBitwiseTakable;
147+
}
133148
}
134149
} else if (auto likeArray = rawLayout->getResolvedArrayLikeTypeAndCount(sd)) {
135150
auto elementType = likeArray->first;

lib/Parse/ParseDecl.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3989,14 +3989,24 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
39893989
if (likeType.isNull()) {
39903990
return makeParserSuccess();
39913991
}
3992+
3993+
bool movesAsLike = false;
3994+
3995+
// @_rawLayout(like: T, movesAsLike)
3996+
if (consumeIf(tok::comma) && Tok.isAny(tok::identifier) &&
3997+
!parseSpecificIdentifier("movesAsLike",
3998+
diag::attr_rawlayout_expected_label, "movesAsLike")) {
3999+
movesAsLike = true;
4000+
}
4001+
39924002
SourceLoc rParenLoc;
39934003
if (!consumeIf(tok::r_paren, rParenLoc)) {
39944004
diagnose(Tok.getLoc(), diag::attr_expected_rparen,
39954005
AttrName, /*isModifier*/false);
39964006
return makeParserSuccess();
39974007
}
39984008

3999-
attr = new (Context) RawLayoutAttr(likeType.get(),
4009+
attr = new (Context) RawLayoutAttr(likeType.get(), movesAsLike,
40004010
AtLoc, SourceRange(Loc, rParenLoc));
40014011
} else if (firstLabel.is("likeArrayOf")) {
40024012
// @_rawLayout(likeArrayOf: T, count: N)

lib/Serialization/Deserialization.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6292,8 +6292,9 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
62926292
TypeID typeID;
62936293
uint32_t rawSize;
62946294
uint8_t rawAlign;
6295+
bool movesAsLike;
62956296
serialization::decls_block::RawLayoutDeclAttrLayout::
6296-
readRecord(scratch, isImplicit, typeID, rawSize, rawAlign);
6297+
readRecord(scratch, isImplicit, typeID, rawSize, rawAlign, movesAsLike);
62976298

62986299
if (typeID) {
62996300
auto type = MF.getTypeChecked(typeID);
@@ -6303,6 +6304,7 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
63036304
auto typeRepr = new (ctx) FixedTypeRepr(type.get(), SourceLoc());
63046305
if (rawAlign == 0) {
63056306
Attr = new (ctx) RawLayoutAttr(typeRepr,
6307+
movesAsLike,
63066308
SourceLoc(),
63076309
SourceRange());
63086310
break;

lib/Serialization/ModuleFormat.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2165,7 +2165,8 @@ namespace decls_block {
21652165
BCFixed<1>, // implicit
21662166
TypeIDField, // like type
21672167
BCVBR<32>, // size
2168-
BCVBR<8> // alignment
2168+
BCVBR<8>, // alignment
2169+
BCFixed<1> // movesAsLike
21692170
>;
21702171

21712172
using SwiftNativeObjCRuntimeBaseDeclAttrLayout = BCRecordLayout<

lib/Serialization/Serialization.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3345,7 +3345,7 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
33453345

33463346
RawLayoutDeclAttrLayout::emitRecord(
33473347
S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(),
3348-
typeID, rawSize, rawAlign);
3348+
typeID, rawSize, rawAlign, attr->shouldMoveAsLikeType());
33493349
}
33503350
}
33513351
}

test/IRGen/Inputs/module.modulemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,7 @@ module PointerAuth {
3939
module CFBridgedType {
4040
header "CFBridgedType.h"
4141
}
42+
43+
module RawLayoutCXX {
44+
header "raw_layout_cxx.h"
45+
}

0 commit comments

Comments
 (0)