Skip to content

[ASTGen] ObjC improvements #80150

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 2 commits into from
Mar 21, 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
21 changes: 21 additions & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -2057,6 +2057,13 @@ BridgedKeyPathExpr BridgedKeyPathExpr_createParsed(
BridgedNullableExpr cParsedRoot, BridgedNullableExpr cParsedPath,
bool hasLeadingDot);

SWIFT_NAME("BridgedKeyPathExpr.createParsedPoundKeyPath(_:poundLoc:lParenLoc:"
"names:nameLocs:rParenLoc:)")
BridgedKeyPathExpr BridgedKeyPathExpr_createParsedPoundKeyPath(
BridgedASTContext cContext, BridgedSourceLoc cPoundLoc,
BridgedSourceLoc cLParenLoc, BridgedArrayRef cNames,
BridgedArrayRef cNameLocs, BridgedSourceLoc cRParenLoc);

SWIFT_NAME("BridgedMacroExpansionExpr.createParsed(_:poundLoc:macroNameRef:"
"macroNameLoc:leftAngleLoc:genericArgs:rightAngleLoc:args:)")
BridgedMacroExpansionExpr BridgedMacroExpansionExpr_createParsed(
Expand Down Expand Up @@ -2087,6 +2094,20 @@ BridgedNilLiteralExpr
BridgedNilLiteralExpr_createParsed(BridgedASTContext cContext,
BridgedSourceLoc cNilKeywordLoc);

enum ENUM_EXTENSIBILITY_ATTR(open) BridgedObjCSelectorKind {
BridgedObjCSelectorKindMethod,
BridgedObjCSelectorKindGetter,
BridgedObjCSelectorKindSetter,
};

SWIFT_NAME("BridgedObjCSelectorExpr.createParsed(_:kind:keywordLoc:lParenLoc:"
"modifierLoc:subExpr:rParenLoc:)")
BridgedObjCSelectorExpr BridgedObjCSelectorExpr_createParsed(
BridgedASTContext cContext, BridgedObjCSelectorKind cKind,
BridgedSourceLoc cKeywordLoc, BridgedSourceLoc cLParenLoc,
BridgedSourceLoc cModifierLoc, BridgedExpr cSubExpr,
BridgedSourceLoc cRParenLoc);

enum ENUM_EXTENSIBILITY_ATTR(open) BridgedObjectLiteralKind : size_t {
#define POUND_OBJECT_LITERAL(Name, Desc, Proto) BridgedObjectLiteralKind_##Name,
#include "swift/AST/TokenKinds.def"
Expand Down
42 changes: 42 additions & 0 deletions lib/AST/Bridging/ExprBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,26 @@ BridgedKeyPathExpr BridgedKeyPathExpr_createParsed(
cParsedPath.unbridged(), hasLeadingDot);
}

BridgedKeyPathExpr BridgedKeyPathExpr_createParsedPoundKeyPath(
BridgedASTContext cContext, BridgedSourceLoc cPoundLoc,
BridgedSourceLoc cLParenLoc, BridgedArrayRef cNames,
BridgedArrayRef cNameLocs, BridgedSourceLoc cRParenLoc) {

SmallVector<KeyPathExpr::Component> components;
auto cNameArr = cNames.unbridged<BridgedDeclNameRef>();
auto cNameLocArr = cNameLocs.unbridged<BridgedDeclNameLoc>();
for (size_t i = 0, e = cNameArr.size(); i != e; ++i) {
auto name = cNameArr[i].unbridged();
auto loc = cNameLocArr[i].unbridged().getBaseNameLoc();
components.push_back(KeyPathExpr::Component::forUnresolvedMember(
name, FunctionRefInfo::unappliedBaseName(), loc));
}

return KeyPathExpr::createParsedPoundKeyPath(
cContext.unbridged(), cPoundLoc.unbridged(), cLParenLoc.unbridged(),
components, cRParenLoc.unbridged());
}

BridgedSuperRefExpr
BridgedSuperRefExpr_createParsed(BridgedASTContext cContext,
BridgedSourceLoc cSuperLoc) {
Expand Down Expand Up @@ -445,6 +465,28 @@ BridgedNilLiteralExpr_createParsed(BridgedASTContext cContext,
return new (cContext.unbridged()) NilLiteralExpr(cNilKeywordLoc.unbridged());
}

BridgedObjCSelectorExpr BridgedObjCSelectorExpr_createParsed(
BridgedASTContext cContext, BridgedObjCSelectorKind cKind,
BridgedSourceLoc cKeywordLoc, BridgedSourceLoc cLParenLoc,
BridgedSourceLoc cModifierLoc, BridgedExpr cSubExpr,
BridgedSourceLoc cRParenLoc) {
ObjCSelectorExpr::ObjCSelectorKind kind;
switch (cKind) {
case BridgedObjCSelectorKindMethod:
kind = ObjCSelectorExpr::Method;
break;
case BridgedObjCSelectorKindGetter:
kind = ObjCSelectorExpr::Getter;
break;
case BridgedObjCSelectorKindSetter:
kind = ObjCSelectorExpr::Setter;
break;
}
return new (cContext.unbridged()) ObjCSelectorExpr(
kind, cKeywordLoc.unbridged(), cLParenLoc.unbridged(),
cModifierLoc.unbridged(), cSubExpr.unbridged(), cRParenLoc.unbridged());
}

SWIFT_NAME("BridgedObjectLiteralKind.init(from:)")
BridgedObjectLiteralKind
BridgedObjectLiteralKind_fromString(BridgedStringRef cStr) {
Expand Down
13 changes: 13 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,19 @@ void Decl::attachParsedAttrs(DeclAttributes attrs) {
for (auto attr : attrs.getAttributes<ABIAttr, /*AllowInvalid=*/true>())
recordABIAttr(attr);

// @implementation requires an explicit @objc attribute, but
// @_objcImplementation didn't. Insert one if necessary.
auto implAttr = attrs.getAttribute<ObjCImplementationAttr>();
if (implAttr && isa<ExtensionDecl>(this) && implAttr->isEarlyAdopter() &&
!attrs.hasAttribute<ObjCAttr>()) {
ObjCAttr *objcAttr =
implAttr->CategoryName.empty()
? ObjCAttr::createUnnamedImplicit(getASTContext())
: ObjCAttr::createNullary(getASTContext(), implAttr->CategoryName,
/*isNameImplicit=*/false);
attrs.add(objcAttr);
}

getAttrs() = attrs;
}

Expand Down
125 changes: 119 additions & 6 deletions lib/ASTGen/Sources/ASTGen/BuiltinPound.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ extension ASTGenVisitor {
switch keyword {
case .selector:
let selectorExpr = self.generateObjCSelectorExpr(freestandingMacroExpansion: node)
return .generated(.expr(selectorExpr.asExpr))
return .generated(.expr(selectorExpr))

case .keyPath:
let keypathExpr = self.generateObjCKeyPathExpr(freestandingMacroExpansion: node)
return .generated(.expr(keypathExpr.asExpr))
return .generated(.expr(keypathExpr))

case .assert where ctx.langOptsHasFeature(.StaticAssert):
let assertStmtOpt = self.generatePoundAssertStmt(freestandingMacroExpansion: node)
Expand Down Expand Up @@ -119,6 +119,15 @@ extension ASTGenVisitor {
// return
}

guard
node.genericArgumentClause == nil,
node.trailingClosure == nil,
node.additionalTrailingClosures.isEmpty
else {
// TODO: Diagnose.
fatalError("#error/#warning with generic specialization")
}

guard node.arguments.count == 1,
let arg = node.arguments.first,
arg.label == nil,
Expand Down Expand Up @@ -194,11 +203,115 @@ extension ASTGenVisitor {
)
}

func generateObjCSelectorExpr(freestandingMacroExpansion node: some FreestandingMacroExpansionSyntax) -> BridgedObjCSelectorExpr {
fatalError("unimplemented (objc selector)")
func generateObjCSelectorExpr(freestandingMacroExpansion node: some FreestandingMacroExpansionSyntax) -> BridgedExpr {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we also need to diagnose the presence of trailing closure + generic arguments?

guard
node.genericArgumentClause == nil,
node.trailingClosure == nil,
node.additionalTrailingClosures.isEmpty
else {
// TODO: Diagnose.
fatalError("#selector with generic specialization")
}

var args = node.arguments[...]
guard let arg = args.popFirst() else {
// TODO: Diagnose
fatalError("expected an argument for #selector")
// return ErrorExpr
}
let kind: BridgedObjCSelectorKind
switch arg.label?.rawText {
case nil: kind = .method
case "getter": kind = .getter
case "setter": kind = .setter
case _?:
// TODO: Diagnose
fatalError("unexpected argument label in #selector")
// return ErrorExpr
}
let expr = self.generate(expr: arg.expression)
guard args.isEmpty else {
// TODO: Diagnose
fatalError("unexpected argument in #selector")
// return ErrorExpr
}
return BridgedObjCSelectorExpr.createParsed(
self.ctx,
kind: kind,
keywordLoc: self.generateSourceLoc(node.pound),
lParenLoc: self.generateSourceLoc(node.leftParen),
modifierLoc: self.generateSourceLoc(arg.label),
subExpr: expr,
rParenLoc: self.generateSourceLoc(node.rightParen)
).asExpr
}

func generateObjCKeyPathExpr(freestandingMacroExpansion node: some FreestandingMacroExpansionSyntax) -> BridgedKeyPathExpr {
fatalError("unimplemented (objc keypath)")
func generateObjCKeyPathExpr(freestandingMacroExpansion node: some FreestandingMacroExpansionSyntax) -> BridgedExpr {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

guard
node.genericArgumentClause == nil,
node.trailingClosure == nil,
node.additionalTrailingClosures.isEmpty
else {
// TODO: Diagnose.
fatalError("#keyPath with generic specialization")
}

var names: [BridgedDeclNameRef] = []
var nameLocs: [BridgedDeclNameLoc] = []

func collectNames(expr node: ExprSyntax) -> Bool {
if let declRefExpr = node.as(DeclReferenceExprSyntax.self) {
let nameAndLoc = self.generateDeclNameRef(declReferenceExpr: declRefExpr)
names.append(nameAndLoc.name)
nameLocs.append(nameAndLoc.loc)
return false
}
if let memberExpr = node.as(MemberAccessExprSyntax.self) {
guard let base = memberExpr.base else {
// TODO: Diagnose
fatalError("unexpected expression in #keyPath")
}
if collectNames(expr: base) {
return true
}
let nameAndLoc = self.generateDeclNameRef(declReferenceExpr: memberExpr.declName)
names.append(nameAndLoc.name)
nameLocs.append(nameAndLoc.loc)
return false
}
// TODO: Diagnose
fatalError("unexpected expression in #keyPath")
// return true
}

var args = node.arguments[...]
guard let arg = args.popFirst() else {
// TODO: Diagnose
fatalError("expected an argument for #keyPath")
// return ErrorExpr
}
guard arg.label == nil else {
// TODO: Diagnose
fatalError("unexpected argument label #keyPath")

}
if /*hadError=*/collectNames(expr: arg.expression) {
return BridgedErrorExpr.create(self.ctx, loc: self.generateSourceRange(node)).asExpr;
}

guard args.isEmpty else {
// TODO: Diagnose
fatalError("unexpected argument in #keyPath")
// return ErrorExpr
}

return BridgedKeyPathExpr.createParsedPoundKeyPath(
self.ctx,
poundLoc: self.generateSourceLoc(node.pound),
lParenLoc: self.generateSourceLoc(node.leftParen),
names: names.lazy.bridgedArray(in: self),
nameLocs: nameLocs.lazy.bridgedArray(in: self),
rParenLoc: self.generateSourceLoc(node.rightParen)
).asExpr
}
}
14 changes: 0 additions & 14 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7070,20 +7070,6 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) {
status |= whereStatus;
}

// @implementation requires an explicit @objc attribute, but
// @_objcImplementation didn't. Insert one if necessary.
auto implAttr = Attributes.getAttribute<ObjCImplementationAttr>();
if (implAttr && implAttr->isEarlyAdopter()
&& !Attributes.hasAttribute<ObjCAttr>()) {
ObjCAttr *objcAttr;
if (implAttr->CategoryName.empty())
objcAttr = ObjCAttr::createUnnamedImplicit(Context);
else
objcAttr = ObjCAttr::createNullary(Context, implAttr->CategoryName,
/*isNameImplicit=*/false);
Attributes.add(objcAttr);
}

ExtensionDecl *ext = ExtensionDecl::create(Context, ExtensionLoc,
extendedType.getPtrOrNull(),
Context.AllocateCopy(Inherited),
Expand Down
3 changes: 3 additions & 0 deletions test/ASTGen/Inputs/objc_decls.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
@import Foundation;

@interface ObjCClass: NSObject
@property NSString *theValue;
-(void)methodWithX:(NSInteger)x Y:(NSInteger)y;
@end

@interface ObjCClass(Category1)
@end
@interface ObjCClass(Category2)
Expand Down
19 changes: 15 additions & 4 deletions test/ASTGen/attrs_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,25 @@
@objc(barWithX:Y:) func foo(x: Int, y: Int) {}
}

@objc @implementation extension ObjCClass {}
@objc @implementation extension ObjCClass {
var theValue: String? {
get { "" }
set {}
}
@objc(methodWithX:Y:)
func methodWith(x: Int, y: Int) {}
}
@objc @implementation(Category1) extension ObjCClass {} // expected-error {{Objective-C category should be specified on '@objc', not '@implementation'}}
@objc(Category2) @implementation extension ObjCClass {}

// FIXME: @_objcImplementation inserts implicit @objc attribute in C++ parser.
//@_objcImplementation extension ObjCClass2 {} // xpected-error {{cannot find type 'ObjCClass2' in scope}}
//@_objcImplementation(Category) extension ObjCClass2 {} // xpected-error {{cannot find type 'ObjCClass2' in scope}}
@_objcImplementation extension ObjCClass2 {} // expected-warning {{'@_objcImplementation' is deprecated; use '@implementation' instead}}

@_objcRuntimeName(RenamedClass) class ThisWillBeRenamed {}

@_swift_native_objc_runtime_base(NSMagicBase) class TestNativeObjCRuntimeBase {}

func testPoundObjC() {
let _: String = #keyPath(ObjCClass.theValue)
let _: Selector = #selector(getter:ObjCClass.theValue)
let _: Selector = #selector(ObjCClass.methodWith(x:y:))
}