Skip to content

[ASTGen] Generate '@_rawLayout' attribute #79629

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
Feb 26, 2025
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
17 changes: 17 additions & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,23 @@ BridgedRawDocCommentAttr
BridgedRawDocCommentAttr_createParsed(BridgedASTContext cContext,
BridgedCharSourceRange cRange);

SWIFT_NAME("BridgedRawLayoutAttr.createParsed(_:atLoc:range:size:alignment:)")
BridgedRawLayoutAttr BridgedStorageRestrictionsAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc cAtLoc,
BridgedSourceRange cRange, size_t size, size_t alignment);

SWIFT_NAME("BridgedRawLayoutAttr.createParsed(_:atLoc:range:like:moveAsLike:)")
BridgedRawLayoutAttr BridgedStorageRestrictionsAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc cAtLoc,
BridgedSourceRange cRange, BridgedTypeRepr cLikeType, bool moveAsLike);

SWIFT_NAME("BridgedRawLayoutAttr.createParsed(_:atLoc:range:likeArrayOf:count:"
"moveAsLike:)")
BridgedRawLayoutAttr BridgedStorageRestrictionsAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc cAtLoc,
BridgedSourceRange cRange, BridgedTypeRepr cLikeType,
BridgedTypeRepr cCountType, bool moveAsLike);

enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedReferenceOwnership {
BridgedReferenceOwnershipStrong,
BridgedReferenceOwnershipWeak,
Expand Down
27 changes: 27 additions & 0 deletions lib/AST/Bridging/DeclAttributeBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,33 @@ BridgedRawDocCommentAttr_createParsed(BridgedASTContext cContext,
return new (cContext.unbridged()) RawDocCommentAttr(cRange.unbridged());
}

BridgedRawLayoutAttr BridgedStorageRestrictionsAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc cAtLoc,
BridgedSourceRange cRange, size_t size, size_t alignment) {
return new (cContext.unbridged())
RawLayoutAttr(size, alignment, cAtLoc.unbridged(), cRange.unbridged());
}

SWIFT_NAME("BridgedRawLayoutAttr.createParsed(_:atLoc:range:like:moveAsLike:)")
BridgedRawLayoutAttr BridgedStorageRestrictionsAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc cAtLoc,
BridgedSourceRange cRange, BridgedTypeRepr cLikeType, bool moveAsLike) {
return new (cContext.unbridged())
RawLayoutAttr(cLikeType.unbridged(), moveAsLike, cAtLoc.unbridged(),
cRange.unbridged());
}

SWIFT_NAME("BridgedRawLayoutAttr.createParsed(_:atLoc:range:likeArrayOf:count:"
"moveAsLike:)")
BridgedRawLayoutAttr BridgedStorageRestrictionsAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc cAtLoc,
BridgedSourceRange cRange, BridgedTypeRepr cLikeType,
BridgedTypeRepr cCountType, bool moveAsLike) {
return new (cContext.unbridged())
RawLayoutAttr(cLikeType.unbridged(), cCountType.unbridged(), moveAsLike,
cAtLoc.unbridged(), cRange.unbridged());
}

ReferenceOwnership unbridged(BridgedReferenceOwnership kind) {
switch (kind) {
case BridgedReferenceOwnershipStrong:
Expand Down
138 changes: 137 additions & 1 deletion lib/ASTGen/Sources/ASTGen/DeclAttrs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ extension ASTGenVisitor {
case .projectedValueProperty:
return handle(self.generateProjectedValuePropertyAttr(attribute: node)?.asDeclAttribute)
case .rawLayout:
fatalError("unimplemented")
return handle(self.generateRawLayoutAttr(attribute: node)?.asDeclAttribute)
case .section:
return handle(self.generateSectionAttr(attribute: node)?.asDeclAttribute)
case .semantics:
Expand Down Expand Up @@ -1445,6 +1445,142 @@ extension ASTGenVisitor {
)
}

func generateValueOrType(expr node: ExprSyntax) -> BridgedTypeRepr? {
var node = node

// Try value first.
let minusLoc: BridgedSourceLoc
if let prefixExpr = node.as(PrefixOperatorExprSyntax.self),
prefixExpr.operator.rawText == "-",
prefixExpr.expression.is(IntegerLiteralExprSyntax.self) {
minusLoc = self.generateSourceLoc(prefixExpr.operator)
node = prefixExpr.expression
} else {
minusLoc = nil
}
if let integerExpr = node.as(IntegerLiteralExprSyntax.self) {
let value = self.copyAndStripUnderscores(text: integerExpr.literal.rawText)
return BridgedIntegerTypeRepr.createParsed(
self.ctx,
string: value,
loc: self.generateSourceLoc(node), minusLoc: minusLoc
).asTypeRepr
}

assert(!minusLoc.isValid)
return self.generateTypeRepr(expr: node)
}

func generateRawLayoutAttr(attribute node: AttributeSyntax) -> BridgedRawLayoutAttr? {
self.generateWithLabeledExprListArguments(attribute: node) { args in
switch args.first?.label?.rawText {
case "size":
return generateSizeAlignment()
case "like":
return generateScalarLike()
case "likeArrayOf":
return generateArrayLike()
default:
// TODO: Diagnose.
fatalError("invalid argument for @rawLayout attribute")
}

func generateSizeAlignment() -> BridgedRawLayoutAttr? {
guard let size = generateConsumingIntegerLiteralOption(label: "size") else {
// Should already be diagnosed.
return nil
}
guard let alignment = generateConsumingIntegerLiteralOption(label: "alignment") else {
// Should already be diagnosed.
return nil
}
return .createParsed(
self.ctx,
atLoc: self.generateSourceLoc(node.atSign),
range: self.generateAttrSourceRange(node),
size: size,
alignment: alignment
)
}

func generateScalarLike() -> BridgedRawLayoutAttr? {
let tyR = self.generateConsumingAttrOption(args: &args, label: "like") {
self.generateTypeRepr(expr: $0)
}
guard let tyR else {
return nil
}

guard let moveAsLike = args.isEmpty ? false : generateConsumingMoveAsLike() else {
return nil
}

return .createParsed(
self.ctx,
atLoc: self.generateSourceLoc(node.atSign),
range: self.generateAttrSourceRange(node),
like: tyR,
moveAsLike: moveAsLike
)
}

func generateArrayLike() -> BridgedRawLayoutAttr? {
let tyR = self.generateConsumingAttrOption(args: &args, label: "likeArrayOf") {
self.generateTypeRepr(expr: $0)
}
guard let tyR else {
return nil
}

// 'count:' can be integer literal or a generic parameter.
let count = self.generateConsumingAttrOption(args: &args, label: "count") {
self.generateValueOrType(expr: $0)
}
guard let count else {
return nil
}

guard let moveAsLike = args.isEmpty ? false : generateConsumingMoveAsLike() else {
return nil
}

return .createParsed(
self.ctx,
atLoc: self.generateSourceLoc(node.atSign),
range: self.generateAttrSourceRange(node),
likeArrayOf: tyR,
count: count,
moveAsLike: moveAsLike
)
}

func generateConsumingIntegerLiteralOption(label: SyntaxText) -> Int? {
self.generateConsumingAttrOption(args: &args, label: label) {
guard let integerExpr = $0.as(IntegerLiteralExprSyntax.self) else {
// TODO: Diagnose
fatalError("expected integer literal for '\(String(syntaxText: label)):' in @_rawLayout")
}
guard let count = integerExpr.representedLiteralValue else {
fatalError("invalid value literal for '\(String(syntaxText: label)):' in @_rawLayout")
}
return count
}
}

func generateConsumingMoveAsLike() -> Bool? {
self.generateConsumingPlainIdentifierAttrOption(args: &args) {
switch $0.rawText {
case "moveAsLike":
return true
default:
// TODO: Diagnose.
fatalError("expected 'moveAsLike' in @rawLayout attribute")
}
}
}
}
}

// FIXME: This is a decl modifier
func generateReferenceOwnershipAttr(attribute node: AttributeSyntax, attrName: SyntaxText)
-> BridgedReferenceOwnershipAttr?
Expand Down
98 changes: 98 additions & 0 deletions lib/ASTGen/Sources/ASTGen/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,101 @@ extension ASTGenVisitor {
}.bridgedArray(in: self)
}
}

extension ASTGenVisitor {
struct GeneratedGenericArguments {
var arguments: BridgedArrayRef = .init()
var range: BridgedSourceRange = .init()
}

/// Generate 'TypeRepr' from a expression, because 'conformances' arguments in
/// macro role attributes are parsed as normal expressions.
func generateTypeRepr(
expr node: ExprSyntax,
genericArgs: GeneratedGenericArguments = GeneratedGenericArguments()
) -> BridgedTypeRepr? {
if !genericArgs.arguments.isEmpty {
guard node.is(MemberAccessExprSyntax.self) || node.is(DeclReferenceExprSyntax.self) else {
// TODO: Diagnose.
fatalError("generic arguments cannot be applied")
}
}

switch node.as(ExprSyntaxEnum.self) {

case .typeExpr(let node):
return self.generate(type: node.type)

case .declReferenceExpr(let node):
guard node.argumentNames == nil else {
// 'Foo.bar(_:baz:)'
break
}
let name = self.generateIdentifierAndSourceLoc(node.baseName)
return BridgedUnqualifiedIdentTypeRepr .createParsed(
self.ctx,
name: name.identifier,
nameLoc: name.sourceLoc,
genericArgs: genericArgs.arguments,
leftAngleLoc: genericArgs.range.start,
rightAngleLoc: genericArgs.range.end
).asTypeRepr

case .memberAccessExpr(let node):
guard let parsedBase = node.base else {
// Implicit member expressions. E.g. '.Foo'
break
}
guard let base = self.generateTypeRepr(expr: parsedBase) else {
// Unsupported base expr. E.g. 'foo().bar'
return nil
}
guard node.declName.argumentNames == nil else {
// Function name. E.g. 'Foo.bar(_:baz:)'
break
}
let name = self.generateIdentifierAndSourceLoc(node.declName.baseName)
return BridgedDeclRefTypeRepr.createParsed(
self.ctx,
base: base,
name: name.identifier,
nameLoc: name.sourceLoc,
genericArguments: genericArgs.arguments,
angleRange: genericArgs.range
).asTypeRepr

case .genericSpecializationExpr(let node):
let args = node.genericArgumentClause.arguments.lazy.map {
self.generate(genericArgument: $0.argument)
}
return self.generateTypeRepr(
expr: node.expression,
genericArgs: GeneratedGenericArguments(
arguments: args.bridgedArray(in: self),
range: self.generateSourceRange(node.genericArgumentClause)
)
)

case .sequenceExpr(let node):
// TODO: Support composition type?
_ = node
break

case .tupleExpr(let node):
// TODO: Support tuple type?
_ = node
break

case .arrowExpr(let node):
// TODO: Support function type?
_ = node
break

default:
break
}

// TODO: Diagnose
fatalError("invalid/unimplemented expression for type")
}
}
15 changes: 15 additions & 0 deletions test/ASTGen/attrs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// RUN: -enable-experimental-feature ExecutionAttribute \
// RUN: -enable-experimental-feature Extern \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: -enable-experimental-feature RawLayout \
// RUN: -enable-experimental-feature SymbolLinkageMarkers \
// RUN: -enable-experimental-move-only \
// RUN: -enable-experimental-feature ParserASTGen \
Expand All @@ -15,6 +16,7 @@
// RUN: -enable-experimental-feature ExecutionAttribute \
// RUN: -enable-experimental-feature Extern \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: -enable-experimental-feature RawLayout \
// RUN: -enable-experimental-feature SymbolLinkageMarkers \
// RUN: -enable-experimental-move-only \
// RUN: | %sanitize-address > %t/cpp-parser.ast
Expand All @@ -28,6 +30,7 @@
// RUN: -enable-experimental-feature ExecutionAttribute \
// RUN: -enable-experimental-feature Extern \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: -enable-experimental-feature RawLayout \
// RUN: -enable-experimental-feature SymbolLinkageMarkers \
// RUN: -enable-experimental-move-only

Expand All @@ -38,6 +41,7 @@
// REQUIRES: swift_feature_ExecutionAttribute
// REQUIRES: swift_feature_Extern
// REQUIRES: swift_feature_LifetimeDependence
// REQUIRES: swift_feature_RawLayout
// REQUIRES: swift_feature_SymbolLinkageMarkers

// rdar://116686158
Expand Down Expand Up @@ -234,3 +238,14 @@ struct ReferenceOwnershipModifierTest<X: AnyObject> {
unowned(safe) var unownedSafeValue: X
unowned(unsafe) var unmanagedValue: X
}

@_rawLayout(like: T) struct RawStorage<T>: ~Copyable {}
@_rawLayout(likeArrayOf: T, count: 4) struct RawSmallArray<T>: ~Copyable {}
@_rawLayout(size: 4, alignment: 4) struct Lock: ~Copyable {}

struct LayoutOuter {
struct Nested<T> {
var value: T
}
}
@_rawLayout(like: LayoutOuter.Nested<Int>) struct TypeExprTest: ~Copyable {}