Skip to content

Swiftify anonymous params rel #81399

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
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
35 changes: 32 additions & 3 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9014,10 +9014,11 @@ class SwiftifyInfoPrinter {
static const ssize_t SELF_PARAM_INDEX = -2;
static const ssize_t RETURN_VALUE_INDEX = -1;
clang::ASTContext &ctx;
ASTContext &SwiftContext;
llvm::raw_ostream &out;
bool firstParam = true;
SwiftifyInfoPrinter(clang::ASTContext &ctx, llvm::raw_ostream &out)
: ctx(ctx), out(out) {
SwiftifyInfoPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext, llvm::raw_ostream &out)
: ctx(ctx), SwiftContext(SwiftContext), out(out) {
out << "@_SwiftifyImport(";
}
~SwiftifyInfoPrinter() { out << ")"; }
Expand Down Expand Up @@ -9074,7 +9075,34 @@ class SwiftifyInfoPrinter {
out << "]";
}

void printAvailability() {
printSeparator();
out << "spanAvailability: ";
printAvailabilityOfType("Span");
}

private:
ValueDecl *getDecl(StringRef DeclName) {
SmallVector<ValueDecl *, 1> decls;
SwiftContext.lookupInSwiftModule(DeclName, decls);
assert(decls.size() == 1);
if (decls.size() != 1) return nullptr;
return decls[0];
}

void printAvailabilityOfType(StringRef Name) {
ValueDecl *D = getDecl(Name);
out << "\"";
llvm::SaveAndRestore<bool> hasAvailbilitySeparatorRestore(firstParam, true);
for (auto attr : D->getSemanticAvailableAttrs(/*includingInactive=*/true)) {
auto introducedOpt = attr.getIntroduced();
if (!introducedOpt.has_value()) continue;
printSeparator();
out << prettyPlatformString(attr.getPlatform()) << " " << introducedOpt.value();
}
out << "\"";
}

void printSeparator() {
if (!firstParam) {
out << ", ";
Expand Down Expand Up @@ -9128,7 +9156,7 @@ void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
}
return false;
};
SwiftifyInfoPrinter printer(getClangASTContext(), out);
SwiftifyInfoPrinter printer(getClangASTContext(), SwiftContext, out);
bool returnIsStdSpan = registerStdSpanTypeMapping(
MappedDecl->getResultInterfaceType(), ClangDecl->getReturnType());
if (auto CAT =
Expand Down Expand Up @@ -9172,6 +9200,7 @@ void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
}
if (returnIsStdSpan && returnHasLifetimeInfo)
attachMacro = true;
printer.printAvailability();
printer.printTypeMapping(typeMapping);
}

Expand Down
107 changes: 93 additions & 14 deletions lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,19 @@ protocol ParamInfo: CustomStringConvertible {
) -> BoundsCheckedThunkBuilder
}

func getParamName(_ param: FunctionParameterSyntax, _ paramIndex: Int) -> TokenSyntax {
let name = param.secondName ?? param.firstName
if name.trimmed.text == "_" {
return "_param\(raw: paramIndex)"
}
return name
}

func tryGetParamName(_ funcDecl: FunctionDeclSyntax, _ expr: SwiftifyExpr) -> TokenSyntax? {
switch expr {
case .param(let i):
let funcParam = getParam(funcDecl, i - 1)
return funcParam.secondName ?? funcParam.firstName
return getParamName(funcParam, i - 1)
case .`self`:
return .keyword(.self)
default: return nil
Expand Down Expand Up @@ -419,7 +427,12 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
// filter out deleted parameters, i.e. ones where argTypes[i] _contains_ nil
return type == nil || type! != nil
}.map { (i: Int, e: FunctionParameterSyntax) in
e.with(\.type, (argTypes[i] ?? e.type)!)
let param = e.with(\.type, (argTypes[i] ?? e.type)!)
let name = param.secondName ?? param.firstName
if name.trimmed.text == "_" {
return param.with(\.secondName, getParamName(param, i))
}
return param
}
if let last = newParams.popLast() {
newParams.append(last.with(\.trailingComma, nil))
Expand All @@ -437,7 +450,7 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
let functionRef = DeclReferenceExprSyntax(baseName: base.name)
let args: [ExprSyntax] = base.signature.parameterClause.parameters.enumerated()
.map { (i: Int, param: FunctionParameterSyntax) in
let name = param.secondName ?? param.firstName
let name = getParamName(param, i)
let declref = DeclReferenceExprSyntax(baseName: name)
return pointerArgs[i] ?? ExprSyntax(declref)
}
Expand Down Expand Up @@ -647,7 +660,7 @@ extension ParamBoundsThunkBuilder {
}

var name: TokenSyntax {
return param.secondName ?? param.firstName
getParamName(param, index)
}
}

Expand Down Expand Up @@ -828,8 +841,7 @@ struct CountedOrSizedPointerThunkBuilder: ParamBoundsThunkBuilder, PointerBounds
}

func castPointerToOpaquePointer(_ baseAddress: ExprSyntax) throws -> ExprSyntax {
let i = try getParameterIndexForParamName(signature.parameterClause.parameters, name)
let type = peelOptionalType(getParam(signature, i).type)
let type = peelOptionalType(getParam(signature, index).type)
if type.canRepresentBasicType(type: OpaquePointer.self) {
return ExprSyntax("OpaquePointer(\(baseAddress))")
}
Expand Down Expand Up @@ -1069,14 +1081,7 @@ func parseLifetimeDependence(_ enumConstructorExpr: FunctionCallExprSyntax) thro
return (pointer, dependence)
}

func parseTypeMappingParam(_ paramAST: LabeledExprSyntax?) throws -> [String: String]? {
guard let unwrappedParamAST = paramAST else {
return nil
}
let paramExpr = unwrappedParamAST.expression
guard let dictExpr = paramExpr.as(DictionaryExprSyntax.self) else {
return nil
}
func parseStringLiteralDict(_ dictExpr: DictionaryExprSyntax) throws -> [String: String] {
var dict: [String: String] = [:]
switch dictExpr.content {
case .colon(_):
Expand All @@ -1098,6 +1103,45 @@ func parseTypeMappingParam(_ paramAST: LabeledExprSyntax?) throws -> [String: St
return dict
}

func parseStringMappingParam(_ paramAST: LabeledExprSyntax?, paramName: String) throws -> [String: String]? {
guard let unwrappedParamAST = paramAST else {
return nil
}
guard let label = unwrappedParamAST.label else {
return nil
}
if label.trimmed.text != paramName {
return nil
}
let paramExpr = unwrappedParamAST.expression
guard let dictExpr = paramExpr.as(DictionaryExprSyntax.self) else {
return nil
}
return try parseStringLiteralDict(dictExpr)
}

func parseTypeMappingParam(_ paramAST: LabeledExprSyntax?) throws -> [String: String]? {
return try parseStringMappingParam(paramAST, paramName: "typeMappings")
}

func parseSpanAvailabilityParam(_ paramAST: LabeledExprSyntax?) throws -> String? {
guard let unwrappedParamAST = paramAST else {
return nil
}
guard let label = unwrappedParamAST.label else {
return nil
}
if label.trimmed.text != "spanAvailability" {
return nil
}
let paramExpr = unwrappedParamAST.expression
guard let stringLitExpr = paramExpr.as(StringLiteralExprSyntax.self) else {
throw DiagnosticError(
"expected a string literal, got '\(paramExpr)'", node: paramExpr)
}
return stringLitExpr.representedLiteralValue
}

func parseCxxSpansInSignature(
_ signature: FunctionSignatureSyntax,
_ typeMappings: [String: String]?
Expand Down Expand Up @@ -1316,6 +1360,35 @@ func isMutableSpan(_ type: TypeSyntax) -> Bool {
return name == "MutableSpan" || name == "MutableRawSpan"
}

func isAnySpan(_ type: TypeSyntax) -> Bool {
if let optType = type.as(OptionalTypeSyntax.self) {
return isAnySpan(optType.wrappedType)
}
if let impOptType = type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) {
return isAnySpan(impOptType.wrappedType)
}
if let attrType = type.as(AttributedTypeSyntax.self) {
return isAnySpan(attrType.baseType)
}
guard let identifierType = type.as(IdentifierTypeSyntax.self) else {
return false
}
let name = identifierType.name.text
return name == "Span" || name == "RawSpan" || name == "MutableSpan" || name == "MutableRawSpan"
}

func getAvailability(_ newSignature: FunctionSignatureSyntax, _ spanAvailability: String?)
throws -> [AttributeListSyntax.Element] {
guard let spanAvailability else {
return []
}
let returnIsSpan = newSignature.returnClause != nil && isAnySpan(newSignature.returnClause!.type)
if !returnIsSpan && !newSignature.parameterClause.parameters.contains(where: { isAnySpan($0.type) }) {
return []
}
return [.attribute(AttributeSyntax("@available(\(raw: spanAvailability), *)"))]
}

func containsLifetimeAttr(_ attrs: AttributeListSyntax, for paramName: TokenSyntax) -> Bool {
for elem in attrs {
guard let attr = elem.as(AttributeSyntax.self) else {
Expand Down Expand Up @@ -1386,6 +1459,10 @@ public struct SwiftifyImportMacro: PeerMacro {
if typeMappings != nil {
arguments = arguments.dropLast()
}
let spanAvailability = try parseSpanAvailabilityParam(arguments.last)
if spanAvailability != nil {
arguments = arguments.dropLast()
}
var nonescapingPointers = Set<Int>()
var lifetimeDependencies: [SwiftifyExpr: [LifetimeDependence]] = [:]
var parsedArgs = try arguments.compactMap {
Expand Down Expand Up @@ -1441,6 +1518,7 @@ public struct SwiftifyImportMacro: PeerMacro {
let returnLifetimeAttribute = getReturnLifetimeAttribute(funcDecl, lifetimeDependencies)
let lifetimeAttrs =
returnLifetimeAttribute + paramLifetimeAttributes(newSignature, funcDecl.attributes)
let availabilityAttr = try getAvailability(newSignature, spanAvailability)
let disfavoredOverload: [AttributeListSyntax.Element] =
(onlyReturnTypeChanged
? [
Expand Down Expand Up @@ -1469,6 +1547,7 @@ public struct SwiftifyImportMacro: PeerMacro {
atSign: .atSignToken(),
attributeName: IdentifierTypeSyntax(name: "_alwaysEmitIntoClient")))
]
+ availabilityAttr
+ lifetimeAttrs
+ disfavoredOverload)
return [DeclSyntax(newFunc)]
Expand Down
6 changes: 4 additions & 2 deletions stdlib/public/core/SwiftifyImport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,15 @@ public enum _SwiftifyInfo {
/// It will replace some std::span arguments with Swift's Span type when sufficient information is
/// available.
///
/// Currently not supported: return pointers, nested pointers, pointee "count" parameters, endedBy.
/// Currently not supported: nested pointers, pointee "count" parameters, endedBy.
///
/// Parameter paramInfo: information about how the function uses the pointer passed to it. The
/// safety of the generated wrapper function depends on this info being extensive and accurate.
#if hasFeature(Macros)
@attached(peer, names: overloaded)
public macro _SwiftifyImport(_ paramInfo: _SwiftifyInfo..., typeMappings: [String: String] = [:]) =
public macro _SwiftifyImport(_ paramInfo: _SwiftifyInfo...,
spanAvailability: String? = nil,
typeMappings: [String: String] = [:]) =
#externalMacro(module: "SwiftMacros", type: "SwiftifyImportMacro")
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
void simple(int len, int * __counted_by(len) __noescape p);

void swiftAttr(int len, int *p) __attribute__((
swift_attr("@_SwiftifyImport(.countedBy(pointer: .param(2), count: \"len\"), .nonescaping(pointer: .param(2)))")));
swift_attr("@_SwiftifyImport(.countedBy(pointer: .param(2), count: \"len\"), .nonescaping(pointer: .param(2)), spanAvailability: \"visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4\")")));

void shared(int len, int * __counted_by(len) __noescape p1, int * __counted_by(len) __noescape p2);

Expand All @@ -22,3 +22,5 @@ void nullable(int len, int * __counted_by(len) _Nullable p __noescape);
int * __counted_by(len) __noescape returnPointer(int len);

int * __counted_by(len1) returnLifetimeBound(int len1, int len2, int * __counted_by(len2) p __lifetimebound);

void anonymous(int len, int * __counted_by(len) _Nullable __noescape);
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
void simple(int len, const void * __sized_by(len) __noescape p);

void swiftAttr(int len, const void *p) __attribute__((swift_attr(
"@_SwiftifyImport(.sizedBy(pointer: .param(2), size: \"len\"), .nonescaping(pointer: .param(2)))")));
"@_SwiftifyImport(.sizedBy(pointer: .param(2), size: \"len\"), .nonescaping(pointer: .param(2)), spanAvailability: \"visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4\")")));

void shared(int len, const void * __sized_by(len) __noescape p1, const void * __sized_by(len) __noescape p2);

Expand Down
Loading