From b794bdc03e448365a5d31b0002d8bec03ade3be6 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Thu, 10 Oct 2024 15:40:50 -0700 Subject: [PATCH 01/13] Add @PointerBounds macro @PointerBounds is a macro intended to be applied by ClangImporter when importing functions with pointer parameters from C headers. By leveraging C attributes we can get insight into bounds, esapability, and (eventually) lifetimes of pointers, allowing us to map them to safe(r) and more ergonomic types than UnsafePointer. This initial macro implementation supports CountedBy and Sizedby, but not yet EndedBy. It can generate function overloads with and without an explicit count parameter, as well as with UnsafeBufferPointer or Span (if marked nonescaping), and any of their combinations. It supports nullable/optional pointers, and both mutable and immutable pointers. It supports arbitrary count expressions. These are passed to the macro as a string literal since any parameters referred to in the count expression will not have been declared yet when parsing the macro. It does not support indirect pointers or inout parameters. It supports functions with return values, but returned pointers can not be bounds checked yet. Bounds checked pointers must be of type Unsafe[Mutable]Pointer[?] or Unsafe[Mutable]RawPointer[?]. Count expressions must conform to the BinaryInteger protocol, and have an initializer with signature "init(exactly: Int) -> T?" (or accept a supertype of Int). rdar://137628612 --- CMakeLists.txt | 5 + lib/Macros/Sources/SwiftMacros/CMakeLists.txt | 1 + .../SwiftMacros/PointerBoundsMacro.swift | 617 ++++++++++++++++++ stdlib/cmake/modules/SwiftSource.cmake | 4 + stdlib/private/StdlibUnittest/CMakeLists.txt | 3 + stdlib/public/CMakeLists.txt | 4 + stdlib/public/PointerBounds/CMakeLists.txt | 13 + .../public/PointerBounds/PointerBounds.swift | 36 + test/CMakeLists.txt | 5 + .../PointerBounds/CountedBy/CountExpr.swift | 19 + .../CountedBy/MultipleParams.swift | 14 + .../PointerBounds/CountedBy/Mutable.swift | 15 + .../PointerBounds/CountedBy/MutableSpan.swift | 17 + .../PointerBounds/CountedBy/Nullable.swift | 14 + .../PointerBounds/CountedBy/Return.swift | 14 + .../PointerBounds/CountedBy/SharedCount.swift | 22 + .../PointerBounds/CountedBy/SimpleCount.swift | 14 + .../PointerBounds/CountedBy/SimpleSpan.swift | 16 + .../CountedBy/SimpleSpanWithReturn.swift | 16 + .../PointerBounds/MacroErrors/ArrayType.swift | 11 + .../MacroErrors/DynamicCountExpr.swift | 12 + .../MacroErrors/DynamicEnum.swift | 12 + .../MacroErrors/DynamicPointerIndex.swift | 12 + .../MacroErrors/InvalidCount.swift | 11 + .../MacroErrors/PointerIndexOutOfBounds.swift | 22 + .../MacroErrors/SamePointer.swift | 11 + .../MacroErrors/UnexpectedCountExpr.swift | 11 + .../MacroErrors/UnexpectedCountType.swift | 19 + .../MacroErrors/UnexpectedPointerType.swift | 15 + .../SizedBy/MultipleParams.swift | 14 + .../PointerBounds/SizedBy/Mutable.swift | 14 + .../SizedBy/MutableRawSpan.swift | 16 + .../PointerBounds/SizedBy/Nullable.swift | 14 + .../Macros/PointerBounds/SizedBy/Return.swift | 14 + .../PointerBounds/SizedBy/SharedCount.swift | 22 + .../PointerBounds/SizedBy/SimpleRawSpan.swift | 16 + .../SizedBy/SimpleRawSpanWithReturn.swift | 16 + .../PointerBounds/SizedBy/SimpleSize.swift | 14 + .../PointerBounds/SizedBy/SizeExpr.swift | 18 + test/lit.cfg | 7 + test/lit.site.cfg.in | 2 + utils/build-script-impl | 7 + utils/build.ps1 | 2 + .../build_swift/driver_arguments.py | 4 + utils/build_swift/tests/expected_options.py | 1 + .../products/minimalstdlib.py | 2 + .../swift_build_support/products/swift.py | 8 + .../products/wasmstdlib.py | 1 + validation-test/lit.site.cfg.in | 2 + 49 files changed, 1179 insertions(+) create mode 100644 lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift create mode 100644 stdlib/public/PointerBounds/CMakeLists.txt create mode 100644 stdlib/public/PointerBounds/PointerBounds.swift create mode 100644 test/Macros/PointerBounds/CountedBy/CountExpr.swift create mode 100644 test/Macros/PointerBounds/CountedBy/MultipleParams.swift create mode 100644 test/Macros/PointerBounds/CountedBy/Mutable.swift create mode 100644 test/Macros/PointerBounds/CountedBy/MutableSpan.swift create mode 100644 test/Macros/PointerBounds/CountedBy/Nullable.swift create mode 100644 test/Macros/PointerBounds/CountedBy/Return.swift create mode 100644 test/Macros/PointerBounds/CountedBy/SharedCount.swift create mode 100644 test/Macros/PointerBounds/CountedBy/SimpleCount.swift create mode 100644 test/Macros/PointerBounds/CountedBy/SimpleSpan.swift create mode 100644 test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/ArrayType.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/InvalidCount.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/SamePointer.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift create mode 100644 test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift create mode 100644 test/Macros/PointerBounds/SizedBy/MultipleParams.swift create mode 100644 test/Macros/PointerBounds/SizedBy/Mutable.swift create mode 100644 test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift create mode 100644 test/Macros/PointerBounds/SizedBy/Nullable.swift create mode 100644 test/Macros/PointerBounds/SizedBy/Return.swift create mode 100644 test/Macros/PointerBounds/SizedBy/SharedCount.swift create mode 100644 test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift create mode 100644 test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift create mode 100644 test/Macros/PointerBounds/SizedBy/SimpleSize.swift create mode 100644 test/Macros/PointerBounds/SizedBy/SizeExpr.swift diff --git a/CMakeLists.txt b/CMakeLists.txt index 0885b3785ff0a..f93c026e41a0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -747,6 +747,10 @@ option(SWIFT_ENABLE_EXPERIMENTAL_PARSER_VALIDATION "Enable experimental SwiftParser validation by default" FALSE) +option(SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS + "Enable experimental safe wrappers around external functions" + FALSE) + cmake_dependent_option(SWIFT_BUILD_SOURCEKIT "Build SourceKit" TRUE "SWIFT_ENABLE_DISPATCH" FALSE) @@ -1399,6 +1403,7 @@ if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_SDK_OVERLAY) message(STATUS "Observation Support: ${SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION}") message(STATUS "Synchronization Support: ${SWIFT_ENABLE_SYNCHRONIZATION}") message(STATUS "Volatile Support: ${SWIFT_ENABLE_VOLATILE}") + message(STATUS "Pointer Bounds Support: ${SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS}") message(STATUS "") else() message(STATUS "Not building Swift standard library, SDK overlays, and runtime") diff --git a/lib/Macros/Sources/SwiftMacros/CMakeLists.txt b/lib/Macros/Sources/SwiftMacros/CMakeLists.txt index 9fc3f26ada073..5c5ab1bd4b616 100644 --- a/lib/Macros/Sources/SwiftMacros/CMakeLists.txt +++ b/lib/Macros/Sources/SwiftMacros/CMakeLists.txt @@ -16,6 +16,7 @@ add_swift_macro_library(SwiftMacros DistributedResolvableMacro.swift SyntaxExtensions.swift TaskLocalMacro.swift + PointerBoundsMacro.swift SWIFT_DEPENDENCIES SwiftDiagnostics SwiftSyntax diff --git a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift new file mode 100644 index 0000000000000..dceefc54e7e7c --- /dev/null +++ b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift @@ -0,0 +1,617 @@ +import SwiftDiagnostics +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +protocol ParamInfo: CustomStringConvertible { + var description: String { get } + var original: ExprSyntax { get } + var pointerIndex: Int { get } + var nonescaping: Bool { get set } + + func getBoundsCheckedThunkBuilder(_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax, + _ variant: Variant + ) -> BoundsCheckedThunkBuilder +} + +struct CountedBy: ParamInfo { + var pointerIndex: Int + var count: ExprSyntax + var sizedBy: Bool + var nonescaping: Bool + var original: ExprSyntax + + var description: String { + if sizedBy { + return ".sizedBy(pointer: \(pointerIndex), size: \"\(count)\", nonescaping: \(nonescaping))" + } + return ".countedBy(pointer: \(pointerIndex), count: \"\(count)\", nonescaping: \(nonescaping))" + } + + func getBoundsCheckedThunkBuilder(_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax, + _ variant: Variant + ) -> BoundsCheckedThunkBuilder { + let funcParam = getParam(funcDecl, pointerIndex - 1) + let paramName = funcParam.secondName ?? funcParam.firstName + let isNullable = funcParam.type.is(OptionalTypeSyntax.self) + return CountedOrSizedPointerThunkBuilder(base: base, index: pointerIndex - 1, countExpr: count, + name: paramName, nullable: isNullable, signature: funcDecl.signature, nonescaping: nonescaping, isSizedBy: sizedBy) + } +} +struct EndedBy: ParamInfo { + var pointerIndex: Int + var endIndex: Int + var nonescaping: Bool + var original: ExprSyntax + + var description: String { + return ".endedBy(start: \(pointerIndex), end: \(endIndex), nonescaping: \(nonescaping))" + } + + func getBoundsCheckedThunkBuilder(_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax, + _ variant: Variant + ) -> BoundsCheckedThunkBuilder { + let funcParam = getParam(funcDecl, pointerIndex - 1) + let paramName = funcParam.secondName ?? funcParam.firstName + let isNullable = funcParam.type.is(OptionalTypeSyntax.self) + return EndedByPointerThunkBuilder(base: base, startIndex: pointerIndex - 1, endIndex: endIndex - 1, + name: paramName, nullable: isNullable, signature: funcDecl.signature, nonescaping: nonescaping) + } +} + +struct RuntimeError: Error { + let description: String + + init(_ description: String) { + self.description = description + } + + var errorDescription: String? { + description + } +} + +struct DiagnosticError: Error { + let description: String + let node: SyntaxProtocol + let notes: [Note] + + init(_ description: String, node: SyntaxProtocol, notes: [Note] = []) { + self.description = description + self.node = node + self.notes = notes + } + + var errorDescription: String? { + description + } +} + +enum UnsafePointerKind { + case Immutable + case Mutable +} + +func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) throws -> TypeSyntax { + if let optType = prev.as(OptionalTypeSyntax.self) { + return TypeSyntax(optType.with(\.wrappedType, try transformType(optType.wrappedType, variant, isSizedBy))) + } + guard let idType = prev.as(IdentifierTypeSyntax.self) else { + throw DiagnosticError("expected pointer type, got \(prev) with kind \(prev.kind)", node: prev) + } + let text = idType.name.text + let kind: UnsafePointerKind = switch text { + case "UnsafePointer": .Immutable + case "UnsafeMutablePointer": .Mutable + case "UnsafeRawPointer": .Immutable + case "UnsafeMutableRawPointer": .Mutable + default: throw DiagnosticError("expected Unsafe[Mutable][Raw]Pointer type for type \(prev)" + + " - first type token is '\(text)'", node: idType.name) + } + if isSizedBy { + let token: TokenSyntax = switch (kind, variant.generateSpan) { + case (.Immutable, true): "RawSpan" + case (.Mutable, true): "MutableRawSpan" + case (.Immutable, false): "UnsafeRawBufferPointer" + case (.Mutable, false): "UnsafeMutableRawBufferPointer" + } + return TypeSyntax(IdentifierTypeSyntax(name: token)) + } + if text == "UnsafeRawPointer" || text == "UnsafeMutableRawPointer" { + throw DiagnosticError("raw pointers only supported for SizedBy", node: idType.name) + } + let token: TokenSyntax = switch (kind, variant.generateSpan) { + case (.Immutable, true): "Span" + case (.Mutable, true): "MutableSpan" + case (.Immutable, false): "UnsafeBufferPointer" + case (.Mutable, false): "UnsafeMutableBufferPointer" + } + return TypeSyntax(idType.with(\.name, token)) +} + +struct Variant { + public let generateSpan: Bool + public let skipTrivialCount: Bool +} + +protocol BoundsCheckedThunkBuilder { + func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax + func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] + func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws -> FunctionSignatureSyntax +} + +func getParam(_ signature: FunctionSignatureSyntax, _ paramIndex: Int) -> FunctionParameterSyntax { + let params = signature.parameterClause.parameters + let index = if paramIndex > 0 { + params.index(params.startIndex, offsetBy: paramIndex) + } else { + params.startIndex + } + return params[index] +} +func getParam(_ funcDecl: FunctionDeclSyntax, _ paramIndex: Int) -> FunctionParameterSyntax { + return getParam(funcDecl.signature, paramIndex) +} + +struct FunctionCallBuilder: BoundsCheckedThunkBuilder { + let base: FunctionDeclSyntax + init(_ function: FunctionDeclSyntax) { + base = function + } + + func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] { + return [] + } + + func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws -> FunctionSignatureSyntax { + var newParams = base.signature.parameterClause.parameters.enumerated().filter { + let type = argTypes[$0.offset] + // 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 last = newParams.popLast()! + newParams.append(last.with(\.trailingComma, nil)) + + return base.signature.with(\.parameterClause.parameters, FunctionParameterListSyntax(newParams)) + } + + func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax], _: Variant) throws -> ExprSyntax { + 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 declref = DeclReferenceExprSyntax(baseName: name) + return pointerArgs[i] ?? ExprSyntax(declref) + } + let labels: [TokenSyntax?] = base.signature.parameterClause.parameters.map { param in + let firstName = param.firstName + if firstName.trimmed.text == "_" { + return nil + } + return firstName + } + let labeledArgs: [LabeledExprSyntax] = zip(labels, args).enumerated().map { (i, e) in + let (label, arg) = e + var comma: TokenSyntax? = nil + if i < args.count - 1 { + comma = .commaToken() + } + return LabeledExprSyntax(label: label, expression: arg, trailingComma: comma) + } + return ExprSyntax(FunctionCallExprSyntax(calledExpression: functionRef, leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax(labeledArgs), rightParen: .rightParenToken())) + } +} + +func hasReturnType(_ signature: FunctionSignatureSyntax) -> Bool { + let returnType = signature.returnClause?.type.as(IdentifierTypeSyntax.self)?.name.text ?? "Void" + return returnType != "Void" +} + +protocol PointerBoundsThunkBuilder: BoundsCheckedThunkBuilder { + var name: TokenSyntax { get } + var nullable: Bool { get } + var signature: FunctionSignatureSyntax { get } + var nonescaping: Bool { get } +} + +struct CountedOrSizedPointerThunkBuilder: PointerBoundsThunkBuilder { + public let base: BoundsCheckedThunkBuilder + public let index: Int + public let countExpr: ExprSyntax + public let name: TokenSyntax + public let nullable: Bool + public let signature: FunctionSignatureSyntax + public let nonescaping: Bool + public let isSizedBy: Bool + + func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws -> FunctionSignatureSyntax { + var types = argTypes + let param = getParam(signature, index) + types[index] = try transformType(param.type, variant, isSizedBy) + if variant.skipTrivialCount { + if let countVar = countExpr.as(DeclReferenceExprSyntax.self) { + let i = try getParameterIndexForDeclRef(signature.parameterClause.parameters, countVar) + types[i] = nil as TypeSyntax? + } + } + return try base.buildFunctionSignature(types, variant) + } + + func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] { + var res = try base.buildBoundsChecks(variant) + let countName: TokenSyntax = "_\(raw: name)Count" + let count: VariableDeclSyntax = try VariableDeclSyntax("let \(countName): some BinaryInteger = \(countExpr)") + res.append(CodeBlockItemSyntax.Item(count)) + + let countCheck = ExprSyntax(""" + if \(getCount(variant)) < \(countName) || \(countName) < 0 { + fatalError("bounds check failure when calling unsafe function") + } + """) + res.append(CodeBlockItemSyntax.Item(countCheck)) + return res + } + + func unwrapIfNullable(_ expr: ExprSyntax) -> ExprSyntax { + if nullable { + return ExprSyntax(ForceUnwrapExprSyntax(expression: expr)) + } + return expr + } + + func unwrapIfNonnullable(_ expr: ExprSyntax) -> ExprSyntax { + if !nullable { + return ExprSyntax(ForceUnwrapExprSyntax(expression: expr)) + } + return expr + } + + func castIntToTargetType(expr: ExprSyntax, type: TypeSyntax) -> ExprSyntax { + let idType = type.as(IdentifierTypeSyntax.self)! + if idType.name.text == "Int" { + return expr + } + return ExprSyntax("\(type)(exactly: \(expr))!") + } + + func buildUnwrapCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax { + let unwrappedName = TokenSyntax("_\(name)Ptr") + var args = argOverrides + let argExpr = ExprSyntax("\(unwrappedName).baseAddress") + assert(args[index] == nil) + args[index] = unwrapIfNonnullable(argExpr) + let call = try base.buildFunctionCall(args, variant) + let ptrRef = unwrapIfNullable(ExprSyntax(DeclReferenceExprSyntax(baseName: name))) + + let returnKw: String = if hasReturnType(signature) { + "return " + } else { + "" + } + let funcName = isSizedBy ? "withUnsafeBytes" : "withUnsafeBufferPointer" + let unwrappedCall = ExprSyntax(""" + \(ptrRef).\(raw: funcName) { \(unwrappedName) in + \(raw: returnKw)\(call) + } + """) + return unwrappedCall + } + + func getCount(_ variant: Variant) -> ExprSyntax { + let countName = isSizedBy && variant.generateSpan ? "byteCount" : "count" + return if nullable { + ExprSyntax("\(name)?.\(raw: countName) ?? 0") + } else { + ExprSyntax("\(name).\(raw: countName)") + } + } + + func getPointerArg() -> ExprSyntax { + return if nullable { + ExprSyntax("\(name)?.baseAddress") + } else { + ExprSyntax("\(name).baseAddress!") + } + } + + func buildFunctionCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax { + var args = argOverrides + if variant.skipTrivialCount { + assert(countExpr.is(DeclReferenceExprSyntax.self) || countExpr.is(IntegerLiteralExprSyntax.self)) + if let countVar = countExpr.as(DeclReferenceExprSyntax.self) { + let i = try getParameterIndexForDeclRef(signature.parameterClause.parameters, countVar) + assert(args[i] == nil) + args[i] = castIntToTargetType(expr: getCount(variant), type: getParam(signature, i).type) + } + } + assert(args[index] == nil) + if variant.generateSpan { + assert(nonescaping) + let unwrappedCall = try buildUnwrapCall(args, variant) + if nullable { + var nullArgs = args + nullArgs[index] = ExprSyntax(NilLiteralExprSyntax(nilKeyword: .keyword(.nil))) + return ExprSyntax(""" + if \(name) == nil { + \(try base.buildFunctionCall(nullArgs, variant)) + } else { + \(unwrappedCall) + } + """) + } + return unwrappedCall + } + + args[index] = getPointerArg() + return try base.buildFunctionCall(args, variant) + } +} + +struct EndedByPointerThunkBuilder: PointerBoundsThunkBuilder { + public let base: BoundsCheckedThunkBuilder + public let startIndex: Int + public let endIndex: Int + public let name: TokenSyntax + public let nullable: Bool + public let signature: FunctionSignatureSyntax + public let nonescaping: Bool + + func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws -> FunctionSignatureSyntax { + throw RuntimeError("endedBy support not yet implemented") + } + + func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] { + throw RuntimeError("endedBy support not yet implemented") + } + + func buildFunctionCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax { + throw RuntimeError("endedBy support not yet implemented") + } +} + +func getArgumentByName(_ argumentList: LabeledExprListSyntax, _ name: String) throws -> ExprSyntax { + guard let arg = argumentList.first(where: { + return $0.label?.text == name + }) else { + throw DiagnosticError("no argument with name '\(name)' in '\(argumentList)'", node: argumentList) + } + return arg.expression +} + +func getOptionalArgumentByName(_ argumentList: LabeledExprListSyntax, _ name: String) -> ExprSyntax? { + return argumentList.first(where: { + $0.label?.text == name + })?.expression +} + +func getParameterIndexForDeclRef(_ parameterList: FunctionParameterListSyntax, _ ref: DeclReferenceExprSyntax) throws -> Int { + let name = ref.baseName.text + guard let index = parameterList.enumerated().first(where: { (_: Int, param: FunctionParameterSyntax) in + let paramenterName = param.secondName ?? param.firstName + return paramenterName.trimmed.text == name + })?.offset else { + throw DiagnosticError("no parameter with name '\(name)' in '\(parameterList)'", node: ref) + } + return index +} + +/// A macro that adds safe(r) wrappers for functions with unsafe pointer types. +/// Depends on bounds, escapability and lifetime information for each pointer. +/// Intended to map to C attributes like __counted_by, __ended_by and __no_escape, +/// for automatic application by ClangImporter when the C declaration is annotated +/// appropriately. +public struct PointerBoundsMacro: PeerMacro { + static func parseEnumName(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> String { + guard let calledExpr = enumConstructorExpr.calledExpression.as(MemberAccessExprSyntax.self) else { + throw DiagnosticError("expected PointerParam enum literal as argument, got '\(enumConstructorExpr)'", node: enumConstructorExpr) + } + return calledExpr.declName.baseName.text + } + + static func getIntLiteralValue(_ expr: ExprSyntax) throws -> Int { + guard let intLiteral = expr.as(IntegerLiteralExprSyntax.self) else { + throw DiagnosticError("expected integer literal, got '\(expr)'", node: expr) + } + guard let res = Int(intLiteral.literal.text) else { + throw DiagnosticError("expected integer literal, got '\(expr)'", node: expr) + } + return res + } + + static func getBoolLiteralValue(_ expr: ExprSyntax) throws -> Bool { + guard let boolLiteral = expr.as(BooleanLiteralExprSyntax.self) else { + throw DiagnosticError("expected boolean literal, got '\(expr)'", node: expr) + } + guard let res = Bool(boolLiteral.literal.text) else { + throw DiagnosticError("expected bool literal, got '\(expr)'", node: expr) + } + return res + } + + static func parseCountedByEnum(_ enumConstructorExpr: FunctionCallExprSyntax, _ signature: FunctionSignatureSyntax) throws -> ParamInfo { + let argumentList = enumConstructorExpr.arguments + let pointerParamIndexArg = try getArgumentByName(argumentList, "pointer") + let pointerParamIndex: Int = try getIntLiteralValue(pointerParamIndexArg) + let countExprArg = try getArgumentByName(argumentList, "count") + guard let countExprStringLit = countExprArg.as(StringLiteralExprSyntax.self) else { + throw DiagnosticError("expected string literal for 'count' parameter, got \(countExprArg)", node: countExprArg) + } + let unwrappedCountExpr = ExprSyntax(stringLiteral: countExprStringLit.representedLiteralValue!) + if let countVar = unwrappedCountExpr.as(DeclReferenceExprSyntax.self) { + // Perform this lookup here so we can override the position to point to the string literal + // instead of line 1, column 1 + do { + _ = try getParameterIndexForDeclRef(signature.parameterClause.parameters, countVar) + } catch let error as DiagnosticError { + throw DiagnosticError(error.description, node: countExprStringLit, notes: error.notes) + } + } + return CountedBy(pointerIndex: pointerParamIndex, count: unwrappedCountExpr, sizedBy: false, nonescaping: false, original: ExprSyntax(enumConstructorExpr)) + } + + static func parseSizedByEnum(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> ParamInfo { + let argumentList = enumConstructorExpr.arguments + let pointerParamIndexArg = try getArgumentByName(argumentList, "pointer") + let pointerParamIndex: Int = try getIntLiteralValue(pointerParamIndexArg) + let sizeExprArg = try getArgumentByName(argumentList, "size") + guard let sizeExprStringLit = sizeExprArg.as(StringLiteralExprSyntax.self) else { + throw DiagnosticError("expected string literal for 'size' parameter, got \(sizeExprArg)", node: sizeExprArg) + } + let unwrappedCountExpr = ExprSyntax(stringLiteral: sizeExprStringLit.representedLiteralValue!) + return CountedBy(pointerIndex: pointerParamIndex, count: unwrappedCountExpr, sizedBy: true, nonescaping: false, original: ExprSyntax(enumConstructorExpr)) + } + + static func parseEndedByEnum(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> ParamInfo { + let argumentList = enumConstructorExpr.arguments + let startParamIndexArg = try getArgumentByName(argumentList, "start") + let startParamIndex: Int = try getIntLiteralValue(startParamIndexArg) + let endParamIndexArg = try getArgumentByName(argumentList, "end") + let endParamIndex: Int = try getIntLiteralValue(endParamIndexArg) + let nonescapingExprArg = getOptionalArgumentByName(argumentList, "nonescaping") + let nonescaping = if nonescapingExprArg != nil { + try getBoolLiteralValue(nonescapingExprArg!) + } else { + false + } + return EndedBy(pointerIndex: startParamIndex, endIndex: endParamIndex, nonescaping: nonescaping, original: ExprSyntax(enumConstructorExpr)) + } + + static func parseNonEscaping(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> Int { + let argumentList = enumConstructorExpr.arguments + let pointerParamIndexArg = try getArgumentByName(argumentList, "pointer") + let pointerParamIndex: Int = try getIntLiteralValue(pointerParamIndexArg) + return pointerParamIndex + } + + static func parseMacroParam(_ paramAST: LabeledExprSyntax, _ signature: FunctionSignatureSyntax, nonescapingPointers: inout Set) throws -> ParamInfo? { + let paramExpr = paramAST.expression + guard let enumConstructorExpr = paramExpr.as(FunctionCallExprSyntax.self) else { + throw DiagnosticError("expected PointerParam enum literal as argument, got '\(paramExpr)'", node: paramExpr) + } + let enumName = try parseEnumName(enumConstructorExpr) + switch enumName { + case "countedBy": return try parseCountedByEnum(enumConstructorExpr, signature) + case "sizedBy": return try parseSizedByEnum(enumConstructorExpr) + case "endedBy": return try parseEndedByEnum(enumConstructorExpr) + case "nonescaping": + let index = try parseNonEscaping(enumConstructorExpr) + nonescapingPointers.insert(index) + return nil + default: throw DiagnosticError("expected 'countedBy', 'sizedBy', 'endedBy' or 'nonescaping', got '\(enumName)'", node: enumConstructorExpr) + } + } + + static func hasSafeVariants(_ parsedArgs: [ParamInfo]) -> Bool { + return parsedArgs.contains { $0.nonescaping } + } + + static func hasTrivialCountVariants(_ parsedArgs: [ParamInfo]) -> Bool { + let countExprs = parsedArgs.compactMap { switch $0 { + case let c as CountedBy: return c.count + default: return nil + }} + let trivialCounts = countExprs.filter { + $0.is(DeclReferenceExprSyntax.self) || $0 .is(IntegerLiteralExprSyntax.self) + } + // don't generate trivial count variants if there are any non-trivial counts + if trivialCounts.count < countExprs.count { + return false + } + let countVars = trivialCounts.filter { $0.is(DeclReferenceExprSyntax.self) } + let distinctCountVars = Set(countVars.map { + return $0.as(DeclReferenceExprSyntax.self)!.baseName.text + }) + // don't generate trivial count variants if two count expressions refer to the same parameter + return countVars.count == distinctCountVars.count + } + + static func checkArgs(_ args: [ParamInfo], _ funcDecl: FunctionDeclSyntax) throws { + var argByIndex: [Int: ParamInfo] = [:] + let paramCount = funcDecl.signature.parameterClause.parameters.count + try args.forEach { pointerArg in + let i = pointerArg.pointerIndex + if i < 1 || i > paramCount { + let noteMessage = if paramCount > 0 { + "function \(funcDecl.name) has parameter indices 1..\(paramCount)" + } else { + "function \(funcDecl.name) has no parameters" + } + throw DiagnosticError("pointer index out of bounds", node: pointerArg.original, + notes: [Note(node: Syntax(funcDecl.name), message: MacroExpansionNoteMessage(noteMessage))]) + } + if argByIndex[i] != nil { + throw DiagnosticError("multiple PointerParams referring to parameter with index " + + "\(i): \(pointerArg) and \(argByIndex[i]!)", node: pointerArg.original) + } + argByIndex[i] = pointerArg + } + } + + static func setNonescapingPointers(_ args: inout [ParamInfo], _ nonescapingPointers: Set) { + for i in 0...args.count - 1 where nonescapingPointers.contains(args[i].pointerIndex) { + args[i].nonescaping = true + } + } + + public static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + do { + guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else { + throw DiagnosticError("@PointerBounds only works on functions", node: declaration) + } + + let argumentList = node.arguments!.as(LabeledExprListSyntax.self)! + var nonescapingPointers = Set() + var parsedArgs = try argumentList.compactMap { try parseMacroParam($0, funcDecl.signature, nonescapingPointers: &nonescapingPointers) } + setNonescapingPointers(&parsedArgs, nonescapingPointers) + try checkArgs(parsedArgs, funcDecl) + let baseBuilder = FunctionCallBuilder(funcDecl) + + let variant = Variant(generateSpan: hasSafeVariants(parsedArgs), skipTrivialCount: hasTrivialCountVariants(parsedArgs)) + + let builder: BoundsCheckedThunkBuilder = parsedArgs.reduce(baseBuilder, { (prev, parsedArg) in + parsedArg.getBoundsCheckedThunkBuilder(prev, funcDecl, variant) + }) + let newSignature = try builder.buildFunctionSignature([:], variant) + let checks = if variant.skipTrivialCount { + [] as [CodeBlockItemSyntax] + } else { + try builder.buildBoundsChecks(variant).map { e in + CodeBlockItemSyntax(leadingTrivia: "\n", item: e) + } + } + let call = if hasReturnType(funcDecl.signature) { + CodeBlockItemSyntax(item: CodeBlockItemSyntax.Item(ReturnStmtSyntax(returnKeyword: .keyword(.return, trailingTrivia: " "), + expression: try builder.buildFunctionCall([:], variant)))) + } else { + CodeBlockItemSyntax(item: CodeBlockItemSyntax.Item(try builder.buildFunctionCall([:], variant))) + } + let body = CodeBlockSyntax(statements: CodeBlockItemListSyntax(checks + [call])) + let newFunc = funcDecl + .with(\.signature, newSignature) + .with(\.body, body) + .with(\.attributes, funcDecl.attributes.filter { e in + switch e { + case .attribute(let attr): + // don't apply this macro recursively + let name = attr.attributeName.as(IdentifierTypeSyntax.self)?.name.text + return name == nil || name != "PointerBounds" + default: return true + } + }) + return [DeclSyntax(newFunc)] + } catch let error as DiagnosticError { + context.diagnose(Diagnostic(node: error.node, message: MacroExpansionErrorMessage(error.description), + notes: error.notes)) + return [] + } + } +} + diff --git a/stdlib/cmake/modules/SwiftSource.cmake b/stdlib/cmake/modules/SwiftSource.cmake index dfa7b1baec24b..617b55836b08c 100644 --- a/stdlib/cmake/modules/SwiftSource.cmake +++ b/stdlib/cmake/modules/SwiftSource.cmake @@ -326,6 +326,10 @@ function(_add_target_variant_swift_compile_flags list(APPEND result "-D" "SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION") endif() + if(SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) + list(APPEND result "-D" "SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS") + endif() + if(SWIFT_ENABLE_SYNCHRONIZATION) list(APPEND result "-D" "SWIFT_ENABLE_SYNCHRONIZATION") endif() diff --git a/stdlib/private/StdlibUnittest/CMakeLists.txt b/stdlib/private/StdlibUnittest/CMakeLists.txt index 5ebc25ecd3f72..5a2df9f932b42 100644 --- a/stdlib/private/StdlibUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnittest/CMakeLists.txt @@ -32,6 +32,9 @@ endif() if (SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING) list(APPEND swift_stdlib_unittest_modules "_StringProcessing") endif() +if (SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) + list(APPEND swift_stdlib_unittest_modules "_PointerBounds") +endif() add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB # This file should be listed the first. Module name is inferred from the diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index 88b0e2b64c72b..029e696dca959 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -281,6 +281,10 @@ if(SWIFT_BUILD_STDLIB) add_subdirectory(StringProcessing) add_subdirectory(RegexBuilder) endif() + + if(SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) + add_subdirectory(PointerBounds) + endif() endif() if(SWIFT_BUILD_STDLIB AND NOT SWIFT_STDLIB_BUILD_ONLY_CORE_MODULES) diff --git a/stdlib/public/PointerBounds/CMakeLists.txt b/stdlib/public/PointerBounds/CMakeLists.txt new file mode 100644 index 0000000000000..cea144a1ca94f --- /dev/null +++ b/stdlib/public/PointerBounds/CMakeLists.txt @@ -0,0 +1,13 @@ +if (SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) + add_swift_target_library(swift_PointerBounds ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB + PointerBounds.swift + SWIFT_COMPILE_FLAGS + ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + -parse-stdlib + + LINK_FLAGS + "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + + INSTALL_IN_COMPONENT stdlib + ) +endif() diff --git a/stdlib/public/PointerBounds/PointerBounds.swift b/stdlib/public/PointerBounds/PointerBounds.swift new file mode 100644 index 0000000000000..617cd01a8600b --- /dev/null +++ b/stdlib/public/PointerBounds/PointerBounds.swift @@ -0,0 +1,36 @@ +import Swift + +public enum PointerParam { + case countedBy(pointer: Int, count: String) + case sizedBy(pointer: Int, size: String) + case endedBy(start: Int, end: Int) + case nonescaping(pointer: Int) +} + +@attached(peer, names: overloaded) +public macro PointerBounds(_ paramInfo: PointerParam...) = + #externalMacro(module: "SwiftMacros", type: "PointerBoundsMacro") + +// Stub interfaces for testing until Span lands +public struct Span { + public var count: Int + var ptr: UnsafeBufferPointer + public func withUnsafeBufferPointer(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R { + return try body(ptr) + } +} +public protocol RawSpan { + var byteCount: Int { get } + func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R +} +public struct MutableSpan { + public var count: Int + var ptr: UnsafeMutableBufferPointer + public func withUnsafeBufferPointer(_ body: (UnsafeMutableBufferPointer) throws -> R) rethrows -> R { + return try body(ptr) + } +} +public protocol MutableRawSpan { + var byteCount: Int { get } + func withUnsafeBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d6e8a2151d47d..02b7ae89379cc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -202,6 +202,7 @@ normalize_boolean_spelling(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY) normalize_boolean_spelling(SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED) normalize_boolean_spelling(SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING) normalize_boolean_spelling(SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION) +normalize_boolean_spelling(SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) normalize_boolean_spelling(SWIFT_ENABLE_MACCATALYST) normalize_boolean_spelling(SWIFT_RUN_TESTS_WITH_HOST_COMPILER) normalize_boolean_spelling(SWIFT_RUNTIME_ENABLE_LEAK_CHECKER) @@ -458,6 +459,10 @@ foreach(SDK ${SWIFT_SDKS}) list(APPEND LIT_ARGS "--param" "string_processing") endif() + if(SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) + list(APPEND LIT_ARGS "--param" "pointer_bounds") + endif() + if(SWIFT_ENABLE_BACKTRACING) list(APPEND LIT_ARGS "--param" "backtracing") endif() diff --git a/test/Macros/PointerBounds/CountedBy/CountExpr.swift b/test/Macros/PointerBounds/CountedBy/CountExpr.swift new file mode 100644 index 0000000000000..78356a060dc34 --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/CountExpr.swift @@ -0,0 +1,19 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "size * count")) +func myFunc(_ ptr: UnsafePointer, _ size: CInt, _ count: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeBufferPointer, _ size: CInt, _ count: CInt) { +// CHECK-NEXT: let _ptrCount: some BinaryInteger = size * count +// CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { +// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") +// CHECK-NEXT: } +// CHECK-NEXT: myFunc(ptr.baseAddress!, size, count) +// CHECK-NEXT: } + diff --git a/test/Macros/PointerBounds/CountedBy/MultipleParams.swift b/test/Macros/PointerBounds/CountedBy/MultipleParams.swift new file mode 100644 index 0000000000000..713327626554a --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/MultipleParams.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len"), .countedBy(pointer: 3, count: "len2")) +func myFunc(_ ptr: UnsafePointer, _ len: CInt, _ ptr2: UnsafePointer, _ len2: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeBufferPointer, _ ptr2: UnsafeBufferPointer) { +// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!, ptr2.baseAddress!, CInt(exactly: ptr2.count)!) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/Mutable.swift b/test/Macros/PointerBounds/CountedBy/Mutable.swift new file mode 100644 index 0000000000000..61197dedc3eb3 --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/Mutable.swift @@ -0,0 +1,15 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeMutableBufferPointer) { +// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } + diff --git a/test/Macros/PointerBounds/CountedBy/MutableSpan.swift b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift new file mode 100644 index 0000000000000..55317ec1b8f0e --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift @@ -0,0 +1,17 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) +func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { +} + +// CHECK: func myFunc(_ ptr: MutableSpan) { +// CHECK-NEXT: ptr.withUnsafeBufferPointer { _ptrPtr in +// CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } +// CHECK-NEXT: } + diff --git a/test/Macros/PointerBounds/CountedBy/Nullable.swift b/test/Macros/PointerBounds/CountedBy/Nullable.swift new file mode 100644 index 0000000000000..96a3058423ce5 --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/Nullable.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func myFunc(_ ptr: UnsafePointer?, _ len: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeBufferPointer?) { +// CHECK-NEXT: myFunc(ptr?.baseAddress, CInt(exactly: ptr?.count ?? 0)!) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/Return.swift b/test/Macros/PointerBounds/CountedBy/Return.swift new file mode 100644 index 0000000000000..5ff4e227f120b --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/Return.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func myFunc(_ ptr: UnsafePointer, _ len: CInt) -> CInt { +} + +// CHECK: func myFunc(_ ptr: UnsafeBufferPointer) -> CInt { +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SharedCount.swift b/test/Macros/PointerBounds/CountedBy/SharedCount.swift new file mode 100644 index 0000000000000..4234119924f6d --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/SharedCount.swift @@ -0,0 +1,22 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len"), .countedBy(pointer: 2, count: "len")) +func myFunc(_ ptr: UnsafePointer, _ ptr2: UnsafePointer, _ len: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeBufferPointer, _ ptr2: UnsafeBufferPointer, _ len: CInt) { +// CHECK-NEXT: let _ptrCount: some BinaryInteger = len +// CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { +// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") +// CHECK-NEXT: } +// CHECK-NEXT: let _ptr2Count: some BinaryInteger = len +// CHECK-NEXT: if ptr2.count < _ptr2Count || _ptr2Count < 0 { +// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") +// CHECK-NEXT: } +// CHECK-NEXT: myFunc(ptr.baseAddress!, ptr2.baseAddress!, len) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleCount.swift b/test/Macros/PointerBounds/CountedBy/SimpleCount.swift new file mode 100644 index 0000000000000..3d8f88d07500c --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/SimpleCount.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func myFunc(_ ptr: UnsafePointer, _ len: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeBufferPointer) { +// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift b/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift new file mode 100644 index 0000000000000..86eb5a07862ca --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift @@ -0,0 +1,16 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) +func myFunc(_ ptr: UnsafePointer, _ len: CInt) { +} + +// CHECK: func myFunc(_ ptr: Span) { +// CHECK-NEXT: ptr.withUnsafeBufferPointer { _ptrPtr in +// CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift b/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift new file mode 100644 index 0000000000000..22655c57af88b --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift @@ -0,0 +1,16 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) +func myFunc(_ ptr: UnsafePointer, _ len: CInt) -> CInt { +} + +// CHECK: func myFunc(_ ptr: Span) -> CInt { +// CHECK-NEXT: return ptr.withUnsafeBufferPointer { _ptrPtr in +// CHECK-NEXT: return myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/MacroErrors/ArrayType.swift b/test/Macros/PointerBounds/MacroErrors/ArrayType.swift new file mode 100644 index 0000000000000..14f26a8cb46ba --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/ArrayType.swift @@ -0,0 +1,11 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len")) +// expected-error@+1{{expected pointer type, got [CInt] with kind arrayType}} +func myFunc(_ ptr: [CInt], _ len: String) { +} diff --git a/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift b/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift new file mode 100644 index 0000000000000..2a04a4d8a3e8b --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift @@ -0,0 +1,12 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify + +import _PointerBounds + +let countString = "len" +// expected-error@+1{{expected string literal for 'count' parameter, got countString}} +@PointerBounds(.countedBy(pointer: 1, count: countString)) +func myFunc(_ ptr: UnsafePointer, _ len: String) { +} diff --git a/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift b/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift new file mode 100644 index 0000000000000..6cb41e050d69e --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift @@ -0,0 +1,12 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify + +import _PointerBounds + +let countedBy = PointerParam.countedBy(pointer: 1, count: "len") +// expected-error@+1{{expected PointerParam enum literal as argument, got 'countedBy'}} +@PointerBounds(countedBy) +func myFunc(_ ptr: UnsafePointer, _ len: String) { +} diff --git a/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift b/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift new file mode 100644 index 0000000000000..3c4f72843f056 --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift @@ -0,0 +1,12 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify + +import _PointerBounds + +let pointerIndex = 1 +// expected-error@+1{{expected integer literal, got 'pointerIndex'}} +@PointerBounds(.countedBy(pointer: pointerIndex, count: "len")) +func myFunc(_ ptr: UnsafePointer, _ len: String) { +} diff --git a/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift b/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift new file mode 100644 index 0000000000000..d80c64112adad --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift @@ -0,0 +1,11 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify + +import _PointerBounds + +// expected-error@+1{{no parameter with name 'foo' in '_ ptr: UnsafePointer, _ len: CInt'}} +@PointerBounds(.countedBy(pointer: 1, count: "foo")) +func myFunc(_ ptr: UnsafePointer, _ len: CInt) { +} diff --git a/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift b/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift new file mode 100644 index 0000000000000..a38833f7bd240 --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift @@ -0,0 +1,22 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify + +import _PointerBounds + +// expected-error@+1{{pointer index out of bounds}} +@PointerBounds(.countedBy(pointer: 3, count: "len")) +// expected-note@+1{{function myFunc has parameter indices 1..2}} +func myFunc(_ ptr: UnsafePointer, _ len: CInt) { +} +// expected-error@+1{{pointer index out of bounds}} +@PointerBounds(.countedBy(pointer: 0, count: "len")) +// expected-note@+1{{function myFunc2 has parameter indices 1..2}} +func myFunc2(_ ptr: UnsafePointer, _ len: CInt) { +} +// expected-error@+1{{pointer index out of bounds}} +@PointerBounds(.countedBy(pointer: 0, count: "1")) +// expected-note@+1{{function myFunc3 has no parameters}} +func myFunc3() { +} diff --git a/test/Macros/PointerBounds/MacroErrors/SamePointer.swift b/test/Macros/PointerBounds/MacroErrors/SamePointer.swift new file mode 100644 index 0000000000000..e3dbd69d9ecd4 --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/SamePointer.swift @@ -0,0 +1,11 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir + +import _PointerBounds + +// expected-error@+1{{multiple PointerParams referring to parameter with index 1: .countedBy(pointer: 1, count: "dummy", nonescaping: false) and .countedBy(pointer: 1, count: "len", nonescaping: false)}} +@PointerBounds(.countedBy(pointer: 1, count: "len"), .countedBy(pointer: 1, count: "dummy")) +func myFunc(_ ptr: UnsafePointer, _ len: CInt, _ dummy: CInt) { +} diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift new file mode 100644 index 0000000000000..429bd8452cc13 --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift @@ -0,0 +1,11 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify + +import _PointerBounds + +// expected-error@+1{{cannot convert value of type 'Int' to expected argument type 'String'}} +@PointerBounds(.countedBy(pointer: 1, count: 2)) +func myFunc(_ ptr: UnsafePointer, _ len: String) { +} diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift new file mode 100644 index 0000000000000..ca8b9c6b7dde0 --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift @@ -0,0 +1,19 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// XFAIL: * + +// RUN: not %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify + +import _PointerBounds + +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func myFunc(_ ptr: UnsafePointer, _ len: String) { +} + +// CHECK: func myFunc(_ ptr: UnsafeBufferPointer) { +// CHECK-NEXT: myFunc(ptr.baseAddress!, String(exactly: ptr.count)!) +// CHECK-NEXT: } + +// expected-error@PointerBounds:2{{no exact matches in call to initializer}} diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift new file mode 100644 index 0000000000000..c73440957b807 --- /dev/null +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift @@ -0,0 +1,15 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify + +import _PointerBounds + +// expected-error@+2{{expected Unsafe[Mutable][Raw]Pointer type for type CInt - first type token is 'CInt'}} +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func myFunc(_ ptr: CInt, _ len: CInt) { +} +// expected-error@+2{{expected Unsafe[Mutable][Raw]Pointer type for type UnsafeBufferPointer - first type token is 'UnsafeBufferPointer'}} +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func myFunc2(_ ptr: UnsafeBufferPointer, _ len: CInt) { +} diff --git a/test/Macros/PointerBounds/SizedBy/MultipleParams.swift b/test/Macros/PointerBounds/SizedBy/MultipleParams.swift new file mode 100644 index 0000000000000..4cbfb7c5bffa6 --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/MultipleParams.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size"), .sizedBy(pointer: 3, size: "size2")) +func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ ptr2: UnsafeRawPointer, _ size2: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer, _ ptr2: UnsafeRawBufferPointer) { +// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!, ptr2.baseAddress!, CInt(exactly: ptr2.count)!) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Mutable.swift b/test/Macros/PointerBounds/SizedBy/Mutable.swift new file mode 100644 index 0000000000000..0bb0f5d8025ee --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/Mutable.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size")) +func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeMutableRawBufferPointer) { +// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift new file mode 100644 index 0000000000000..ed0783bb7952e --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift @@ -0,0 +1,16 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1)) +func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { +} + +// CHECK: func myFunc(_ ptr: MutableRawSpan) { +// CHECK-NEXT: ptr.withUnsafeBytes { _ptrPtr in +// CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Nullable.swift b/test/Macros/PointerBounds/SizedBy/Nullable.swift new file mode 100644 index 0000000000000..3be17fd10f3d2 --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/Nullable.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size")) +func myFunc(_ ptr: UnsafeRawPointer?, _ size: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer?) { +// CHECK-NEXT: myFunc(ptr?.baseAddress, CInt(exactly: ptr?.count ?? 0)!) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Return.swift b/test/Macros/PointerBounds/SizedBy/Return.swift new file mode 100644 index 0000000000000..1dc299be9d7c1 --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/Return.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size")) +func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) -> CInt { +} + +// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer) -> CInt { +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SharedCount.swift b/test/Macros/PointerBounds/SizedBy/SharedCount.swift new file mode 100644 index 0000000000000..2ef57e5c04328 --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/SharedCount.swift @@ -0,0 +1,22 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size"), .sizedBy(pointer: 2, size: "size")) +func myFunc(_ ptr: UnsafeRawPointer, _ ptr2: UnsafeRawPointer, _ size: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer, _ ptr2: UnsafeRawBufferPointer, _ size: CInt) { +// CHECK-NEXT: let _ptrCount: some BinaryInteger = size +// CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { +// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") +// CHECK-NEXT: } +// CHECK-NEXT: let _ptr2Count: some BinaryInteger = size +// CHECK-NEXT: if ptr2.count < _ptr2Count || _ptr2Count < 0 { +// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") +// CHECK-NEXT: } +// CHECK-NEXT: myFunc(ptr.baseAddress!, ptr2.baseAddress!, size) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift b/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift new file mode 100644 index 0000000000000..9f26a45c47436 --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift @@ -0,0 +1,16 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1)) +func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { +} + +// CHECK: func myFunc(_ ptr: RawSpan) { +// CHECK-NEXT: ptr.withUnsafeBytes { _ptrPtr in +// CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift b/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift new file mode 100644 index 0000000000000..c0d0a6c459f7c --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift @@ -0,0 +1,16 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1)) +func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) -> CInt { +} + +// CHECK: func myFunc(_ ptr: RawSpan) -> CInt { +// CHECK-NEXT: return ptr.withUnsafeBytes { _ptrPtr in +// CHECK-NEXT: return myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleSize.swift b/test/Macros/PointerBounds/SizedBy/SimpleSize.swift new file mode 100644 index 0000000000000..0355a81e5f9b3 --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/SimpleSize.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size")) +func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer) { +// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SizeExpr.swift b/test/Macros/PointerBounds/SizedBy/SizeExpr.swift new file mode 100644 index 0000000000000..e5258557955cc --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/SizeExpr.swift @@ -0,0 +1,18 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +import _PointerBounds + +@PointerBounds(.sizedBy(pointer: 1, size: "size * count")) +func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ count: CInt) { +} + +// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer, _ size: CInt, _ count: CInt) { +// CHECK-NEXT: let _ptrCount: some BinaryInteger = size * count +// CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { +// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") +// CHECK-NEXT: } +// CHECK-NEXT: myFunc(ptr.baseAddress!, size, count) +// CHECK-NEXT: } diff --git a/test/lit.cfg b/test/lit.cfg index ea12fe5c5754c..b68a5e0b5a66f 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -2223,6 +2223,13 @@ config.substitutions.append(('%string_processing_module', string_processing_modu config.substitutions.append(('%/string_processing_module', '/'.join(os.path.normpath(string_processing_module).split(os.sep)))) +# Add 'pointer_bounds_module' as the path to the _PointerBounds .swiftmodule file +pointer_bounds_module = os.path.join(stdlib_dir, "_PointerBounds.swiftmodule", + target_specific_module_triple + ".swiftmodule") +config.substitutions.append(('%pointer_bounds_module', pointer_bounds_module)) +config.substitutions.append(('%/pointer_bounds_module', + '/'.join(os.path.normpath(pointer_bounds_module).split(os.sep)))) + back_deployment_runtime = lit_config.params.get('back_deployment_runtime', None) if back_deployment_runtime is not None: config.available_features.add('back_deployment_runtime') diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 4b4290aa04e0d..ba75d4ed07ea9 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -156,6 +156,8 @@ if "@SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING@" == "TRUE": config.available_features.add('string_processing') if "@SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION@" == "TRUE": config.available_features.add('observation') +if "@SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS@" == "TRUE": + config.available_features.add('pointer_bounds') if "@SWIFT_STDLIB_ENABLE_DEBUG_PRECONDITIONS_IN_RELEASE@" == "TRUE": config.available_features.add('swift_stdlib_debug_preconditions_in_release') if "@SWIFT_ENABLE_VOLATILE@" == "TRUE": diff --git a/utils/build-script-impl b/utils/build-script-impl index 83a3f3ea6ec80..a7040be8f3006 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -2091,6 +2091,13 @@ for host in "${ALL_HOSTS[@]}"; do ) fi + if [[ "${SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS}" ]] ; then + cmake_options=( + "${cmake_options[@]}" + -DSWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS:BOOL=$(true_false "${SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS}") + ) + fi + # SWIFT_THREADING_PACKAGE can be: # # - Empty diff --git a/utils/build.ps1 b/utils/build.ps1 index 913988e5e519f..5dd9259c15c23 100644 --- a/utils/build.ps1 +++ b/utils/build.ps1 @@ -1491,6 +1491,7 @@ function Build-Compilers() { SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED = "YES"; SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION = "YES"; SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING = "YES"; + SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS = "YES"; SWIFT_ENABLE_SYNCHRONIZATION = "YES"; SWIFT_ENABLE_VOLATILE = "YES"; SWIFT_PATH_TO_LIBDISPATCH_SOURCE = "$SourceCache\swift-corelibs-libdispatch"; @@ -1796,6 +1797,7 @@ function Build-Runtime([Platform]$Platform, $Arch) { SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED = "YES"; SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION = "YES"; SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING = "YES"; + SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS = "YES"; SWIFT_ENABLE_SYNCHRONIZATION = "YES"; SWIFT_ENABLE_VOLATILE = "YES"; SWIFT_NATIVE_SWIFT_TOOLS_PATH = (Join-Path -Path $CompilersBinaryCache -ChildPath "bin"); diff --git a/utils/build_swift/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py index e8362fb148d82..e6bddcf8ba6a0 100644 --- a/utils/build_swift/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -1497,6 +1497,10 @@ def create_argument_parser(): default=True, help='Enable experimental Swift Parser validation by default.') + option('--enable-experimental-pointer-bounds', toggle_true, + default=False, + help='Enable experimental bounds safe C interop.') + # ------------------------------------------------------------------------- in_group('Unsupported options') diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index 08b64f7f7db99..ea0add221a89e 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -629,6 +629,7 @@ class BuildScriptImplOption(_BaseOption): EnableOption('--enable-experimental-string-processing'), EnableOption('--enable-experimental-observation'), EnableOption('--enable-experimental-parser-validation'), + EnableOption('--enable-experimental-pointer-bounds'), EnableOption('--enable-lsan'), EnableOption('--enable-sanitize-coverage'), EnableOption('--enable-tsan'), diff --git a/utils/swift_build_support/swift_build_support/products/minimalstdlib.py b/utils/swift_build_support/swift_build_support/products/minimalstdlib.py index 38f0b73402f7b..007f0f6b7f75c 100644 --- a/utils/swift_build_support/swift_build_support/products/minimalstdlib.py +++ b/utils/swift_build_support/swift_build_support/products/minimalstdlib.py @@ -104,6 +104,8 @@ def build(self, host_target): 'SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED:BOOL', 'FALSE') self.cmake_options.define( 'SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION:BOOL', 'FALSE') + self.cmake_options.define( + 'SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS:BOOL', 'FALSE') self.cmake_options.define('SWIFT_ENABLE_REFLECTION:BOOL', 'FALSE') self.cmake_options.define( 'SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS:BOOL', 'FALSE') diff --git a/utils/swift_build_support/swift_build_support/products/swift.py b/utils/swift_build_support/swift_build_support/products/swift.py index b56f116309036..224adfbd0cad2 100644 --- a/utils/swift_build_support/swift_build_support/products/swift.py +++ b/utils/swift_build_support/swift_build_support/products/swift.py @@ -57,6 +57,9 @@ def __init__(self, args, toolchain, source_dir, build_dir): self.cmake_options.extend(self._enable_experimental_cxx_interop) self.cmake_options.extend(self._enable_cxx_interop_swift_bridging_header) + # Add experimental c interop flag. + self.cmake_options.extend(self._enable_experimental_pointer_bounds) + # Add experimental distributed flag. self.cmake_options.extend(self._enable_experimental_distributed) @@ -215,6 +218,11 @@ def _enable_experimental_observation(self): return [('SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION:BOOL', self.args.enable_experimental_observation)] + @property + def _enable_experimental_pointer_bounds(self): + return [('SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS:BOOL', + self.args.enable_experimental_pointer_bounds)] + @property def _enable_synchronization(self): return [('SWIFT_ENABLE_SYNCHRONIZATION:BOOL', diff --git a/utils/swift_build_support/swift_build_support/products/wasmstdlib.py b/utils/swift_build_support/swift_build_support/products/wasmstdlib.py index 70299e3788558..fdd64e2b21319 100644 --- a/utils/swift_build_support/swift_build_support/products/wasmstdlib.py +++ b/utils/swift_build_support/swift_build_support/products/wasmstdlib.py @@ -164,6 +164,7 @@ def _build_stdlib(self, host_target, target_triple, llvm_cmake_dir): self.cmake_options.define('SWIFT_ENABLE_SYNCHRONIZATION:BOOL', 'TRUE') self.cmake_options.define('SWIFT_ENABLE_VOLATILE:BOOL', 'TRUE') self.cmake_options.define('SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION:BOOL', 'TRUE') + self.cmake_options.define('SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS:BOOL', 'TRUE') self.add_extra_cmake_options() diff --git a/validation-test/lit.site.cfg.in b/validation-test/lit.site.cfg.in index c627bb5421b5e..325ca1ce5fe5c 100644 --- a/validation-test/lit.site.cfg.in +++ b/validation-test/lit.site.cfg.in @@ -133,6 +133,8 @@ if "@SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED@" == "TRUE": config.available_features.add('distributed') if "@SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING@" == "TRUE": config.available_features.add('string_processing') +if "@SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS@" == "TRUE": + config.available_features.add('pointer_bounds') if "@SWIFT_STDLIB_ENABLE_DEBUG_PRECONDITIONS_IN_RELEASE@" == "TRUE": config.available_features.add('swift_stdlib_debug_preconditions_in_release') From eca4e00b83246fc27dd44da8024d70a6be3860c4 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Tue, 15 Oct 2024 13:47:35 -0700 Subject: [PATCH 02/13] Don't enable SymbolLinkageMarkers, mark @_alwaysEmitIntoClient --- lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift | 7 ++++--- test/Macros/PointerBounds/CountedBy/CountExpr.swift | 5 +++-- test/Macros/PointerBounds/CountedBy/MultipleParams.swift | 5 +++-- test/Macros/PointerBounds/CountedBy/Mutable.swift | 5 +++-- test/Macros/PointerBounds/CountedBy/MutableSpan.swift | 5 +++-- test/Macros/PointerBounds/CountedBy/Nullable.swift | 5 +++-- test/Macros/PointerBounds/CountedBy/Return.swift | 5 +++-- test/Macros/PointerBounds/CountedBy/SharedCount.swift | 5 +++-- test/Macros/PointerBounds/CountedBy/SimpleCount.swift | 5 +++-- test/Macros/PointerBounds/CountedBy/SimpleSpan.swift | 5 +++-- .../PointerBounds/CountedBy/SimpleSpanWithReturn.swift | 5 +++-- test/Macros/PointerBounds/MacroErrors/ArrayType.swift | 2 +- .../PointerBounds/MacroErrors/DynamicCountExpr.swift | 2 +- test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift | 2 +- .../PointerBounds/MacroErrors/DynamicPointerIndex.swift | 2 +- test/Macros/PointerBounds/MacroErrors/InvalidCount.swift | 2 +- .../MacroErrors/PointerIndexOutOfBounds.swift | 2 +- test/Macros/PointerBounds/MacroErrors/SamePointer.swift | 2 +- .../PointerBounds/MacroErrors/UnexpectedCountExpr.swift | 2 +- .../PointerBounds/MacroErrors/UnexpectedCountType.swift | 7 ++++--- .../PointerBounds/MacroErrors/UnexpectedPointerType.swift | 2 +- test/Macros/PointerBounds/SizedBy/MultipleParams.swift | 5 +++-- test/Macros/PointerBounds/SizedBy/Mutable.swift | 5 +++-- test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift | 5 +++-- test/Macros/PointerBounds/SizedBy/Nullable.swift | 5 +++-- test/Macros/PointerBounds/SizedBy/Return.swift | 5 +++-- test/Macros/PointerBounds/SizedBy/SharedCount.swift | 5 +++-- test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift | 5 +++-- .../PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift | 5 +++-- test/Macros/PointerBounds/SizedBy/SimpleSize.swift | 5 +++-- test/Macros/PointerBounds/SizedBy/SizeExpr.swift | 5 +++-- 31 files changed, 77 insertions(+), 55 deletions(-) diff --git a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift index dceefc54e7e7c..01561e794a299 100644 --- a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift @@ -600,12 +600,13 @@ public struct PointerBoundsMacro: PeerMacro { .with(\.attributes, funcDecl.attributes.filter { e in switch e { case .attribute(let attr): - // don't apply this macro recursively + // don't apply this macro recursively, and avoid dupe _alwaysEmitIntoClient let name = attr.attributeName.as(IdentifierTypeSyntax.self)?.name.text - return name == nil || name != "PointerBounds" + return name == nil || (name != "PointerBounds" && name != "_alwaysEmitIntoClient") default: return true } - }) + } + [.attribute(AttributeSyntax(atSign: .atSignToken(), + attributeName: IdentifierTypeSyntax(name: "_alwaysEmitIntoClient")))]) return [DeclSyntax(newFunc)] } catch let error as DiagnosticError { context.diagnose(Diagnostic(node: error.node, message: MacroExpansionErrorMessage(error.description), diff --git a/test/Macros/PointerBounds/CountedBy/CountExpr.swift b/test/Macros/PointerBounds/CountedBy/CountExpr.swift index 78356a060dc34..b38b3927406fe 100644 --- a/test/Macros/PointerBounds/CountedBy/CountExpr.swift +++ b/test/Macros/PointerBounds/CountedBy/CountExpr.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafePointer, _ size: CInt, _ count: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeBufferPointer, _ size: CInt, _ count: CInt) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer, _ size: CInt, _ count: CInt) { // CHECK-NEXT: let _ptrCount: some BinaryInteger = size * count // CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { // CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") diff --git a/test/Macros/PointerBounds/CountedBy/MultipleParams.swift b/test/Macros/PointerBounds/CountedBy/MultipleParams.swift index 713327626554a..2ec424ea87665 100644 --- a/test/Macros/PointerBounds/CountedBy/MultipleParams.swift +++ b/test/Macros/PointerBounds/CountedBy/MultipleParams.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,6 +9,7 @@ import _PointerBounds func myFunc(_ ptr: UnsafePointer, _ len: CInt, _ ptr2: UnsafePointer, _ len2: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeBufferPointer, _ ptr2: UnsafeBufferPointer) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer, _ ptr2: UnsafeBufferPointer) { // CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!, ptr2.baseAddress!, CInt(exactly: ptr2.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/Mutable.swift b/test/Macros/PointerBounds/CountedBy/Mutable.swift index 61197dedc3eb3..598727e2229dc 100644 --- a/test/Macros/PointerBounds/CountedBy/Mutable.swift +++ b/test/Macros/PointerBounds/CountedBy/Mutable.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeMutableBufferPointer) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeMutableBufferPointer) { // CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/MutableSpan.swift b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift index 55317ec1b8f0e..72ad72b7c93c0 100644 --- a/test/Macros/PointerBounds/CountedBy/MutableSpan.swift +++ b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { } -// CHECK: func myFunc(_ ptr: MutableSpan) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: MutableSpan) { // CHECK-NEXT: ptr.withUnsafeBufferPointer { _ptrPtr in // CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/Nullable.swift b/test/Macros/PointerBounds/CountedBy/Nullable.swift index 96a3058423ce5..b3f4b31258284 100644 --- a/test/Macros/PointerBounds/CountedBy/Nullable.swift +++ b/test/Macros/PointerBounds/CountedBy/Nullable.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,6 +9,7 @@ import _PointerBounds func myFunc(_ ptr: UnsafePointer?, _ len: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeBufferPointer?) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer?) { // CHECK-NEXT: myFunc(ptr?.baseAddress, CInt(exactly: ptr?.count ?? 0)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/Return.swift b/test/Macros/PointerBounds/CountedBy/Return.swift index 5ff4e227f120b..6fd2525ecbcc6 100644 --- a/test/Macros/PointerBounds/CountedBy/Return.swift +++ b/test/Macros/PointerBounds/CountedBy/Return.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,6 +9,7 @@ import _PointerBounds func myFunc(_ ptr: UnsafePointer, _ len: CInt) -> CInt { } -// CHECK: func myFunc(_ ptr: UnsafeBufferPointer) -> CInt { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer) -> CInt { // CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SharedCount.swift b/test/Macros/PointerBounds/CountedBy/SharedCount.swift index 4234119924f6d..30a52321845df 100644 --- a/test/Macros/PointerBounds/CountedBy/SharedCount.swift +++ b/test/Macros/PointerBounds/CountedBy/SharedCount.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafePointer, _ ptr2: UnsafePointer, _ len: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeBufferPointer, _ ptr2: UnsafeBufferPointer, _ len: CInt) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer, _ ptr2: UnsafeBufferPointer, _ len: CInt) { // CHECK-NEXT: let _ptrCount: some BinaryInteger = len // CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { // CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") diff --git a/test/Macros/PointerBounds/CountedBy/SimpleCount.swift b/test/Macros/PointerBounds/CountedBy/SimpleCount.swift index 3d8f88d07500c..5fd6ecc7e8325 100644 --- a/test/Macros/PointerBounds/CountedBy/SimpleCount.swift +++ b/test/Macros/PointerBounds/CountedBy/SimpleCount.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,6 +9,7 @@ import _PointerBounds func myFunc(_ ptr: UnsafePointer, _ len: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeBufferPointer) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer) { // CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift b/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift index 86eb5a07862ca..00139da2cb17c 100644 --- a/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift +++ b/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafePointer, _ len: CInt) { } -// CHECK: func myFunc(_ ptr: Span) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: Span) { // CHECK-NEXT: ptr.withUnsafeBufferPointer { _ptrPtr in // CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift b/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift index 22655c57af88b..a558c3ba0820c 100644 --- a/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift +++ b/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafePointer, _ len: CInt) -> CInt { } -// CHECK: func myFunc(_ ptr: Span) -> CInt { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: Span) -> CInt { // CHECK-NEXT: return ptr.withUnsafeBufferPointer { _ptrPtr in // CHECK-NEXT: return myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/MacroErrors/ArrayType.swift b/test/Macros/PointerBounds/MacroErrors/ArrayType.swift index 14f26a8cb46ba..28cf3a3242e2e 100644 --- a/test/Macros/PointerBounds/MacroErrors/ArrayType.swift +++ b/test/Macros/PointerBounds/MacroErrors/ArrayType.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify import _PointerBounds diff --git a/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift b/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift index 2a04a4d8a3e8b..754f8efde7941 100644 --- a/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift +++ b/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify import _PointerBounds diff --git a/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift b/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift index 6cb41e050d69e..b256baad845db 100644 --- a/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift +++ b/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify import _PointerBounds diff --git a/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift b/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift index 3c4f72843f056..b8432471ef1d3 100644 --- a/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift +++ b/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify import _PointerBounds diff --git a/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift b/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift index d80c64112adad..bd6214bfd8d10 100644 --- a/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift +++ b/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify import _PointerBounds diff --git a/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift b/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift index a38833f7bd240..b0ad2a7a4823f 100644 --- a/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift +++ b/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify import _PointerBounds diff --git a/test/Macros/PointerBounds/MacroErrors/SamePointer.swift b/test/Macros/PointerBounds/MacroErrors/SamePointer.swift index e3dbd69d9ecd4..250ea64f13e15 100644 --- a/test/Macros/PointerBounds/MacroErrors/SamePointer.swift +++ b/test/Macros/PointerBounds/MacroErrors/SamePointer.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir import _PointerBounds diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift index 429bd8452cc13..40fac864274d3 100644 --- a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify import _PointerBounds diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift index ca8b9c6b7dde0..2b82eebff3344 100644 --- a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift @@ -3,8 +3,8 @@ // XFAIL: * -// RUN: not %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify +// RUN: not %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify import _PointerBounds @@ -12,7 +12,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafePointer, _ len: String) { } -// CHECK: func myFunc(_ ptr: UnsafeBufferPointer) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer) { // CHECK-NEXT: myFunc(ptr.baseAddress!, String(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift index c73440957b807..d795fe4ae4c0e 100644 --- a/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -verify +// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify import _PointerBounds diff --git a/test/Macros/PointerBounds/SizedBy/MultipleParams.swift b/test/Macros/PointerBounds/SizedBy/MultipleParams.swift index 4cbfb7c5bffa6..5b7565b37fcbe 100644 --- a/test/Macros/PointerBounds/SizedBy/MultipleParams.swift +++ b/test/Macros/PointerBounds/SizedBy/MultipleParams.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,6 +9,7 @@ import _PointerBounds func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ ptr2: UnsafeRawPointer, _ size2: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer, _ ptr2: UnsafeRawBufferPointer) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer, _ ptr2: UnsafeRawBufferPointer) { // CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!, ptr2.baseAddress!, CInt(exactly: ptr2.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Mutable.swift b/test/Macros/PointerBounds/SizedBy/Mutable.swift index 0bb0f5d8025ee..b32431a3c427b 100644 --- a/test/Macros/PointerBounds/SizedBy/Mutable.swift +++ b/test/Macros/PointerBounds/SizedBy/Mutable.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,6 +9,7 @@ import _PointerBounds func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeMutableRawBufferPointer) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeMutableRawBufferPointer) { // CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift index ed0783bb7952e..52752aa2f114e 100644 --- a/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift +++ b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { } -// CHECK: func myFunc(_ ptr: MutableRawSpan) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: MutableRawSpan) { // CHECK-NEXT: ptr.withUnsafeBytes { _ptrPtr in // CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Nullable.swift b/test/Macros/PointerBounds/SizedBy/Nullable.swift index 3be17fd10f3d2..f1d9919b60aad 100644 --- a/test/Macros/PointerBounds/SizedBy/Nullable.swift +++ b/test/Macros/PointerBounds/SizedBy/Nullable.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,6 +9,7 @@ import _PointerBounds func myFunc(_ ptr: UnsafeRawPointer?, _ size: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer?) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer?) { // CHECK-NEXT: myFunc(ptr?.baseAddress, CInt(exactly: ptr?.count ?? 0)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Return.swift b/test/Macros/PointerBounds/SizedBy/Return.swift index 1dc299be9d7c1..0843e0f395f97 100644 --- a/test/Macros/PointerBounds/SizedBy/Return.swift +++ b/test/Macros/PointerBounds/SizedBy/Return.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,6 +9,7 @@ import _PointerBounds func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) -> CInt { } -// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer) -> CInt { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer) -> CInt { // CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SharedCount.swift b/test/Macros/PointerBounds/SizedBy/SharedCount.swift index 2ef57e5c04328..6b9e8c6f15ff7 100644 --- a/test/Macros/PointerBounds/SizedBy/SharedCount.swift +++ b/test/Macros/PointerBounds/SizedBy/SharedCount.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafeRawPointer, _ ptr2: UnsafeRawPointer, _ size: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer, _ ptr2: UnsafeRawBufferPointer, _ size: CInt) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer, _ ptr2: UnsafeRawBufferPointer, _ size: CInt) { // CHECK-NEXT: let _ptrCount: some BinaryInteger = size // CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { // CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") diff --git a/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift b/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift index 9f26a45c47436..29c6285bdef65 100644 --- a/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift +++ b/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { } -// CHECK: func myFunc(_ ptr: RawSpan) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: RawSpan) { // CHECK-NEXT: ptr.withUnsafeBytes { _ptrPtr in // CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift b/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift index c0d0a6c459f7c..097ee5d2bd0e2 100644 --- a/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift +++ b/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) -> CInt { } -// CHECK: func myFunc(_ ptr: RawSpan) -> CInt { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: RawSpan) -> CInt { // CHECK-NEXT: return ptr.withUnsafeBytes { _ptrPtr in // CHECK-NEXT: return myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleSize.swift b/test/Macros/PointerBounds/SizedBy/SimpleSize.swift index 0355a81e5f9b3..0eab2665451f3 100644 --- a/test/Macros/PointerBounds/SizedBy/SimpleSize.swift +++ b/test/Macros/PointerBounds/SizedBy/SimpleSize.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,6 +9,7 @@ import _PointerBounds func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer) { // CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SizeExpr.swift b/test/Macros/PointerBounds/SizedBy/SizeExpr.swift index e5258557955cc..15a7233950723 100644 --- a/test/Macros/PointerBounds/SizedBy/SizeExpr.swift +++ b/test/Macros/PointerBounds/SizedBy/SizeExpr.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -enable-experimental-feature SymbolLinkageMarkers -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s import _PointerBounds @@ -9,7 +9,8 @@ import _PointerBounds func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ count: CInt) { } -// CHECK: func myFunc(_ ptr: UnsafeRawBufferPointer, _ size: CInt, _ count: CInt) { +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer, _ size: CInt, _ count: CInt) { // CHECK-NEXT: let _ptrCount: some BinaryInteger = size * count // CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { // CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") From d42aa469f992904b0f40df9df073e2a7a81e5689 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Tue, 15 Oct 2024 16:11:46 -0700 Subject: [PATCH 03/13] Document @PointerBounds and PointerParam --- .../public/PointerBounds/PointerBounds.swift | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/stdlib/public/PointerBounds/PointerBounds.swift b/stdlib/public/PointerBounds/PointerBounds.swift index 617cd01a8600b..b2e393cf962d6 100644 --- a/stdlib/public/PointerBounds/PointerBounds.swift +++ b/stdlib/public/PointerBounds/PointerBounds.swift @@ -1,12 +1,46 @@ import Swift +/// Different ways to annotate pointer parameters using the `@PointerBounds` macro. +/// All indices into parameter lists start at 1. Indices __must__ be integer literals, and strings +/// __must__ be string literals, because their contents are parsed by the `@PointerBounds` macro. +/// Only 1 instance of `countedBy`, `sizedBy` or `endedBy` can refer to each pointer index, however +/// `nonescaping` is orthogonal to the rest and can (and should) overlap with other annotations. public enum PointerParam { + /// Corresponds to the C `__counted_by(count)` attribute. + /// Parameter pointer: index of pointer in function parameter list. Must be of type + /// `Unsafe[Mutable]Pointer[?]`, i.e. not an `UnsafeRawPointer`. + /// Parameter count: string containing valid Swift syntax containing the number of elements in + /// the buffer. case countedBy(pointer: Int, count: String) + /// Corresponds to the C `__sized_by(size)` attribute. + /// Parameter pointer: index of pointer in function parameter list. Must be of type + /// `Unsafe[Mutable]RawPointer[?]`, i.e. not an `UnsafePointer`. + /// Parameter count: string containing valid Swift syntax containing the size of the buffer, + /// in bytes. case sizedBy(pointer: Int, size: String) + /// Corresponds to the C `__ended_by(end)` attribute. + /// Parameter start: index of pointer in function parameter list. + /// Parameter end: index of pointer in function parameter list, pointing one past the end of + /// the same buffer as `start`. case endedBy(start: Int, end: Int) + /// Corresponds to the C `noescape` attribute. Allows generated wrapper to use `Span`-types + /// instead of `UnsafeBuffer`-types, because it is known that the function doesn't capture the + /// object past the lifetime of the function. + /// Parameter pointer: index of pointer in function parameter list. case nonescaping(pointer: Int) } +/// Generates a bounds safe wrapper for function with Unsafe[Mutable][Raw]Pointer[?] arguments. +/// Intended to be automatically attached to function declarations imported by ClangImporter. +/// The wrapper function will replace Unsafe[Mutable][Raw]Pointer[?] parameters with +/// [Mutable][Raw]Span[?] or Unsafe[Mutable][Raw]BufferPointer[?] if they have bounds information +/// attached. Where possible "count" parameters will be elided from the wrapper signature, instead +/// fetching the count from the buffer pointer. In these cases the bounds check is also skipped. +/// +/// Currently not supported: return pointers, 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. @attached(peer, names: overloaded) public macro PointerBounds(_ paramInfo: PointerParam...) = #externalMacro(module: "SwiftMacros", type: "PointerBoundsMacro") From 69ec3fcbabb6d3ca5bbdef0c3c465090bf68cd3a Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Mon, 28 Oct 2024 17:28:31 -0700 Subject: [PATCH 04/13] Move @PointerBounds to stdlib core, support Implicitly Unwrapped types --- .../Sources/SwiftMacros/PointerBoundsMacro.swift | 3 +++ stdlib/private/StdlibUnittest/CMakeLists.txt | 3 --- stdlib/public/CMakeLists.txt | 4 ---- stdlib/public/PointerBounds/CMakeLists.txt | 13 ------------- stdlib/public/core/CMakeLists.txt | 5 +++++ stdlib/public/core/GroupInfo.json | 1 + .../{PointerBounds => core}/PointerBounds.swift | 2 -- .../Macros/PointerBounds/CountedBy/CountExpr.swift | 2 -- .../PointerBounds/CountedBy/MultipleParams.swift | 2 -- test/Macros/PointerBounds/CountedBy/Mutable.swift | 2 -- .../PointerBounds/CountedBy/MutableSpan.swift | 2 -- test/Macros/PointerBounds/CountedBy/Nullable.swift | 2 -- test/Macros/PointerBounds/CountedBy/Return.swift | 2 -- .../PointerBounds/CountedBy/SharedCount.swift | 2 -- .../PointerBounds/CountedBy/SimpleCount.swift | 2 -- .../PointerBounds/CountedBy/SimpleSpan.swift | 2 -- .../CountedBy/SimpleSpanWithReturn.swift | 2 -- .../Macros/PointerBounds/CountedBy/Unwrapped.swift | 14 ++++++++++++++ .../PointerBounds/MacroErrors/ArrayType.swift | 2 -- .../MacroErrors/DynamicCountExpr.swift | 2 -- .../PointerBounds/MacroErrors/DynamicEnum.swift | 2 -- .../MacroErrors/DynamicPointerIndex.swift | 2 -- .../PointerBounds/MacroErrors/InvalidCount.swift | 2 -- .../MacroErrors/PointerIndexOutOfBounds.swift | 2 -- .../PointerBounds/MacroErrors/SamePointer.swift | 2 -- .../MacroErrors/UnexpectedCountExpr.swift | 2 -- .../MacroErrors/UnexpectedCountType.swift | 2 -- .../MacroErrors/UnexpectedPointerType.swift | 2 -- .../PointerBounds/SizedBy/MultipleParams.swift | 2 -- test/Macros/PointerBounds/SizedBy/Mutable.swift | 2 -- .../PointerBounds/SizedBy/MutableRawSpan.swift | 2 -- test/Macros/PointerBounds/SizedBy/Nullable.swift | 2 -- test/Macros/PointerBounds/SizedBy/Return.swift | 2 -- .../Macros/PointerBounds/SizedBy/SharedCount.swift | 2 -- .../PointerBounds/SizedBy/SimpleRawSpan.swift | 2 -- .../SizedBy/SimpleRawSpanWithReturn.swift | 2 -- test/Macros/PointerBounds/SizedBy/SimpleSize.swift | 2 -- test/Macros/PointerBounds/SizedBy/SizeExpr.swift | 2 -- test/Macros/PointerBounds/SizedBy/Unwrapped.swift | 14 ++++++++++++++ test/lit.cfg | 7 ------- 40 files changed, 37 insertions(+), 89 deletions(-) delete mode 100644 stdlib/public/PointerBounds/CMakeLists.txt rename stdlib/public/{PointerBounds => core}/PointerBounds.swift (99%) create mode 100644 test/Macros/PointerBounds/CountedBy/Unwrapped.swift create mode 100644 test/Macros/PointerBounds/SizedBy/Unwrapped.swift diff --git a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift index 01561e794a299..fec640c0ec975 100644 --- a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift @@ -97,6 +97,9 @@ func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) th if let optType = prev.as(OptionalTypeSyntax.self) { return TypeSyntax(optType.with(\.wrappedType, try transformType(optType.wrappedType, variant, isSizedBy))) } + if let impOptType = prev.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) { + return try transformType(impOptType.wrappedType, variant, isSizedBy) + } guard let idType = prev.as(IdentifierTypeSyntax.self) else { throw DiagnosticError("expected pointer type, got \(prev) with kind \(prev.kind)", node: prev) } diff --git a/stdlib/private/StdlibUnittest/CMakeLists.txt b/stdlib/private/StdlibUnittest/CMakeLists.txt index 5a2df9f932b42..5ebc25ecd3f72 100644 --- a/stdlib/private/StdlibUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnittest/CMakeLists.txt @@ -32,9 +32,6 @@ endif() if (SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING) list(APPEND swift_stdlib_unittest_modules "_StringProcessing") endif() -if (SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) - list(APPEND swift_stdlib_unittest_modules "_PointerBounds") -endif() add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB # This file should be listed the first. Module name is inferred from the diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index 029e696dca959..88b0e2b64c72b 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -281,10 +281,6 @@ if(SWIFT_BUILD_STDLIB) add_subdirectory(StringProcessing) add_subdirectory(RegexBuilder) endif() - - if(SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) - add_subdirectory(PointerBounds) - endif() endif() if(SWIFT_BUILD_STDLIB AND NOT SWIFT_STDLIB_BUILD_ONLY_CORE_MODULES) diff --git a/stdlib/public/PointerBounds/CMakeLists.txt b/stdlib/public/PointerBounds/CMakeLists.txt deleted file mode 100644 index cea144a1ca94f..0000000000000 --- a/stdlib/public/PointerBounds/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -if (SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) - add_swift_target_library(swift_PointerBounds ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB - PointerBounds.swift - SWIFT_COMPILE_FLAGS - ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - -parse-stdlib - - LINK_FLAGS - "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" - - INSTALL_IN_COMPONENT stdlib - ) -endif() diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 56948d72dcde3..4c59125f7dcfc 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -261,6 +261,11 @@ if(SWIFT_STDLIB_ENABLE_VECTOR_TYPES) list(APPEND SWIFTLIB_EMBEDDED_GYB_SOURCES SIMDConcreteOperations.swift.gyb SIMDVectorTypes.swift.gyb) endif() +if (SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS) + list(APPEND SWIFTLIB_SOURCES PointerBounds.swift) + list(APPEND SWIFTLIB_EMBEDDED_SOURCES PointerBounds.swift) +endif() + # Freestanding and Linux/Android builds both have failures to resolve. if(NOT BOOTSTRAPPING_MODE STREQUAL "OFF" AND NOT SWIFT_FREESTANDING_FLAVOR AND NOT SWIFT_HOST_VARIANT_SDK STREQUAL "LINUX" AND NOT SWIFT_HOST_VARIANT_SDK STREQUAL "ANDROID") list(APPEND SWIFTLIB_SOURCES ObjectIdentifier+DebugDescription.swift) diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index dd218333d84e5..2f4c20d6533ab 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -190,6 +190,7 @@ ], "Pointer": [ "Pointer.swift", + "PointerBounds.swift", "TemporaryAllocation.swift", "UnsafePointer.swift", "UnsafeRawPointer.swift", diff --git a/stdlib/public/PointerBounds/PointerBounds.swift b/stdlib/public/core/PointerBounds.swift similarity index 99% rename from stdlib/public/PointerBounds/PointerBounds.swift rename to stdlib/public/core/PointerBounds.swift index b2e393cf962d6..9cee32a66e210 100644 --- a/stdlib/public/PointerBounds/PointerBounds.swift +++ b/stdlib/public/core/PointerBounds.swift @@ -1,5 +1,3 @@ -import Swift - /// Different ways to annotate pointer parameters using the `@PointerBounds` macro. /// All indices into parameter lists start at 1. Indices __must__ be integer literals, and strings /// __must__ be string literals, because their contents are parsed by the `@PointerBounds` macro. diff --git a/test/Macros/PointerBounds/CountedBy/CountExpr.swift b/test/Macros/PointerBounds/CountedBy/CountExpr.swift index b38b3927406fe..23e6c92126c7e 100644 --- a/test/Macros/PointerBounds/CountedBy/CountExpr.swift +++ b/test/Macros/PointerBounds/CountedBy/CountExpr.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "size * count")) func myFunc(_ ptr: UnsafePointer, _ size: CInt, _ count: CInt) { } diff --git a/test/Macros/PointerBounds/CountedBy/MultipleParams.swift b/test/Macros/PointerBounds/CountedBy/MultipleParams.swift index 2ec424ea87665..10c363df3b74d 100644 --- a/test/Macros/PointerBounds/CountedBy/MultipleParams.swift +++ b/test/Macros/PointerBounds/CountedBy/MultipleParams.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len"), .countedBy(pointer: 3, count: "len2")) func myFunc(_ ptr: UnsafePointer, _ len: CInt, _ ptr2: UnsafePointer, _ len2: CInt) { } diff --git a/test/Macros/PointerBounds/CountedBy/Mutable.swift b/test/Macros/PointerBounds/CountedBy/Mutable.swift index 598727e2229dc..4d4f735d2a120 100644 --- a/test/Macros/PointerBounds/CountedBy/Mutable.swift +++ b/test/Macros/PointerBounds/CountedBy/Mutable.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { } diff --git a/test/Macros/PointerBounds/CountedBy/MutableSpan.swift b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift index 72ad72b7c93c0..8c2d0d237f5c5 100644 --- a/test/Macros/PointerBounds/CountedBy/MutableSpan.swift +++ b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { } diff --git a/test/Macros/PointerBounds/CountedBy/Nullable.swift b/test/Macros/PointerBounds/CountedBy/Nullable.swift index b3f4b31258284..7c9a890ecbeaf 100644 --- a/test/Macros/PointerBounds/CountedBy/Nullable.swift +++ b/test/Macros/PointerBounds/CountedBy/Nullable.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafePointer?, _ len: CInt) { } diff --git a/test/Macros/PointerBounds/CountedBy/Return.swift b/test/Macros/PointerBounds/CountedBy/Return.swift index 6fd2525ecbcc6..2b46ef663990d 100644 --- a/test/Macros/PointerBounds/CountedBy/Return.swift +++ b/test/Macros/PointerBounds/CountedBy/Return.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafePointer, _ len: CInt) -> CInt { } diff --git a/test/Macros/PointerBounds/CountedBy/SharedCount.swift b/test/Macros/PointerBounds/CountedBy/SharedCount.swift index 30a52321845df..7e3019583a1d7 100644 --- a/test/Macros/PointerBounds/CountedBy/SharedCount.swift +++ b/test/Macros/PointerBounds/CountedBy/SharedCount.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len"), .countedBy(pointer: 2, count: "len")) func myFunc(_ ptr: UnsafePointer, _ ptr2: UnsafePointer, _ len: CInt) { } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleCount.swift b/test/Macros/PointerBounds/CountedBy/SimpleCount.swift index 5fd6ecc7e8325..cca16399c35db 100644 --- a/test/Macros/PointerBounds/CountedBy/SimpleCount.swift +++ b/test/Macros/PointerBounds/CountedBy/SimpleCount.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafePointer, _ len: CInt) { } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift b/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift index 00139da2cb17c..26393736e8ba4 100644 --- a/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift +++ b/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafePointer, _ len: CInt) { } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift b/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift index a558c3ba0820c..5edc112828d46 100644 --- a/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift +++ b/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafePointer, _ len: CInt) -> CInt { } diff --git a/test/Macros/PointerBounds/CountedBy/Unwrapped.swift b/test/Macros/PointerBounds/CountedBy/Unwrapped.swift new file mode 100644 index 0000000000000..cb8617054c3b2 --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/Unwrapped.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func myFunc(_ ptr: UnsafePointer!, _ len: CInt) { +} + +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer) { +// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } + diff --git a/test/Macros/PointerBounds/MacroErrors/ArrayType.swift b/test/Macros/PointerBounds/MacroErrors/ArrayType.swift index 28cf3a3242e2e..f6429cb8033e0 100644 --- a/test/Macros/PointerBounds/MacroErrors/ArrayType.swift +++ b/test/Macros/PointerBounds/MacroErrors/ArrayType.swift @@ -3,8 +3,6 @@ // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len")) // expected-error@+1{{expected pointer type, got [CInt] with kind arrayType}} func myFunc(_ ptr: [CInt], _ len: String) { diff --git a/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift b/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift index 754f8efde7941..b70ca9fd06e37 100644 --- a/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift +++ b/test/Macros/PointerBounds/MacroErrors/DynamicCountExpr.swift @@ -3,8 +3,6 @@ // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify -import _PointerBounds - let countString = "len" // expected-error@+1{{expected string literal for 'count' parameter, got countString}} @PointerBounds(.countedBy(pointer: 1, count: countString)) diff --git a/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift b/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift index b256baad845db..04ab7b39dffcf 100644 --- a/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift +++ b/test/Macros/PointerBounds/MacroErrors/DynamicEnum.swift @@ -3,8 +3,6 @@ // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify -import _PointerBounds - let countedBy = PointerParam.countedBy(pointer: 1, count: "len") // expected-error@+1{{expected PointerParam enum literal as argument, got 'countedBy'}} @PointerBounds(countedBy) diff --git a/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift b/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift index b8432471ef1d3..63430a0420707 100644 --- a/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift +++ b/test/Macros/PointerBounds/MacroErrors/DynamicPointerIndex.swift @@ -3,8 +3,6 @@ // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify -import _PointerBounds - let pointerIndex = 1 // expected-error@+1{{expected integer literal, got 'pointerIndex'}} @PointerBounds(.countedBy(pointer: pointerIndex, count: "len")) diff --git a/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift b/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift index bd6214bfd8d10..6c7e8b1683861 100644 --- a/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift +++ b/test/Macros/PointerBounds/MacroErrors/InvalidCount.swift @@ -3,8 +3,6 @@ // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify -import _PointerBounds - // expected-error@+1{{no parameter with name 'foo' in '_ ptr: UnsafePointer, _ len: CInt'}} @PointerBounds(.countedBy(pointer: 1, count: "foo")) func myFunc(_ ptr: UnsafePointer, _ len: CInt) { diff --git a/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift b/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift index b0ad2a7a4823f..2db45850e5928 100644 --- a/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift +++ b/test/Macros/PointerBounds/MacroErrors/PointerIndexOutOfBounds.swift @@ -3,8 +3,6 @@ // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify -import _PointerBounds - // expected-error@+1{{pointer index out of bounds}} @PointerBounds(.countedBy(pointer: 3, count: "len")) // expected-note@+1{{function myFunc has parameter indices 1..2}} diff --git a/test/Macros/PointerBounds/MacroErrors/SamePointer.swift b/test/Macros/PointerBounds/MacroErrors/SamePointer.swift index 250ea64f13e15..198af2f890434 100644 --- a/test/Macros/PointerBounds/MacroErrors/SamePointer.swift +++ b/test/Macros/PointerBounds/MacroErrors/SamePointer.swift @@ -3,8 +3,6 @@ // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -import _PointerBounds - // expected-error@+1{{multiple PointerParams referring to parameter with index 1: .countedBy(pointer: 1, count: "dummy", nonescaping: false) and .countedBy(pointer: 1, count: "len", nonescaping: false)}} @PointerBounds(.countedBy(pointer: 1, count: "len"), .countedBy(pointer: 1, count: "dummy")) func myFunc(_ ptr: UnsafePointer, _ len: CInt, _ dummy: CInt) { diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift index 40fac864274d3..f20f55e1034eb 100644 --- a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountExpr.swift @@ -3,8 +3,6 @@ // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify -import _PointerBounds - // expected-error@+1{{cannot convert value of type 'Int' to expected argument type 'String'}} @PointerBounds(.countedBy(pointer: 1, count: 2)) func myFunc(_ ptr: UnsafePointer, _ len: String) { diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift index 2b82eebff3344..6b04771d6a5aa 100644 --- a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift @@ -6,8 +6,6 @@ // RUN: not %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify -import _PointerBounds - @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafePointer, _ len: String) { } diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift index d795fe4ae4c0e..a70af64b0807e 100644 --- a/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedPointerType.swift @@ -3,8 +3,6 @@ // RUN: %target-typecheck-verify-swift -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify -import _PointerBounds - // expected-error@+2{{expected Unsafe[Mutable][Raw]Pointer type for type CInt - first type token is 'CInt'}} @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: CInt, _ len: CInt) { diff --git a/test/Macros/PointerBounds/SizedBy/MultipleParams.swift b/test/Macros/PointerBounds/SizedBy/MultipleParams.swift index 5b7565b37fcbe..23eebea0d2f11 100644 --- a/test/Macros/PointerBounds/SizedBy/MultipleParams.swift +++ b/test/Macros/PointerBounds/SizedBy/MultipleParams.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size"), .sizedBy(pointer: 3, size: "size2")) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ ptr2: UnsafeRawPointer, _ size2: CInt) { } diff --git a/test/Macros/PointerBounds/SizedBy/Mutable.swift b/test/Macros/PointerBounds/SizedBy/Mutable.swift index b32431a3c427b..cc21ae0758f84 100644 --- a/test/Macros/PointerBounds/SizedBy/Mutable.swift +++ b/test/Macros/PointerBounds/SizedBy/Mutable.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size")) func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { } diff --git a/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift index 52752aa2f114e..f517341a2a968 100644 --- a/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift +++ b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { } diff --git a/test/Macros/PointerBounds/SizedBy/Nullable.swift b/test/Macros/PointerBounds/SizedBy/Nullable.swift index f1d9919b60aad..6132fa5ed537d 100644 --- a/test/Macros/PointerBounds/SizedBy/Nullable.swift +++ b/test/Macros/PointerBounds/SizedBy/Nullable.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size")) func myFunc(_ ptr: UnsafeRawPointer?, _ size: CInt) { } diff --git a/test/Macros/PointerBounds/SizedBy/Return.swift b/test/Macros/PointerBounds/SizedBy/Return.swift index 0843e0f395f97..5475733569bac 100644 --- a/test/Macros/PointerBounds/SizedBy/Return.swift +++ b/test/Macros/PointerBounds/SizedBy/Return.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size")) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) -> CInt { } diff --git a/test/Macros/PointerBounds/SizedBy/SharedCount.swift b/test/Macros/PointerBounds/SizedBy/SharedCount.swift index 6b9e8c6f15ff7..d8e79d05a15a5 100644 --- a/test/Macros/PointerBounds/SizedBy/SharedCount.swift +++ b/test/Macros/PointerBounds/SizedBy/SharedCount.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size"), .sizedBy(pointer: 2, size: "size")) func myFunc(_ ptr: UnsafeRawPointer, _ ptr2: UnsafeRawPointer, _ size: CInt) { } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift b/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift index 29c6285bdef65..cc5951293661c 100644 --- a/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift +++ b/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift b/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift index 097ee5d2bd0e2..8158026ae5d1f 100644 --- a/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift +++ b/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) -> CInt { } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleSize.swift b/test/Macros/PointerBounds/SizedBy/SimpleSize.swift index 0eab2665451f3..92eb44325739c 100644 --- a/test/Macros/PointerBounds/SizedBy/SimpleSize.swift +++ b/test/Macros/PointerBounds/SizedBy/SimpleSize.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size")) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { } diff --git a/test/Macros/PointerBounds/SizedBy/SizeExpr.swift b/test/Macros/PointerBounds/SizedBy/SizeExpr.swift index 15a7233950723..c73f486c94b29 100644 --- a/test/Macros/PointerBounds/SizedBy/SizeExpr.swift +++ b/test/Macros/PointerBounds/SizedBy/SizeExpr.swift @@ -3,8 +3,6 @@ // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s -import _PointerBounds - @PointerBounds(.sizedBy(pointer: 1, size: "size * count")) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ count: CInt) { } diff --git a/test/Macros/PointerBounds/SizedBy/Unwrapped.swift b/test/Macros/PointerBounds/SizedBy/Unwrapped.swift new file mode 100644 index 0000000000000..e78e6677b040f --- /dev/null +++ b/test/Macros/PointerBounds/SizedBy/Unwrapped.swift @@ -0,0 +1,14 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s + +@PointerBounds(.sizedBy(pointer: 1, size: "len")) +func myFunc(_ ptr: UnsafeRawPointer!, _ len: CInt) { +} + +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer) { +// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: } + diff --git a/test/lit.cfg b/test/lit.cfg index b68a5e0b5a66f..ea12fe5c5754c 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -2223,13 +2223,6 @@ config.substitutions.append(('%string_processing_module', string_processing_modu config.substitutions.append(('%/string_processing_module', '/'.join(os.path.normpath(string_processing_module).split(os.sep)))) -# Add 'pointer_bounds_module' as the path to the _PointerBounds .swiftmodule file -pointer_bounds_module = os.path.join(stdlib_dir, "_PointerBounds.swiftmodule", - target_specific_module_triple + ".swiftmodule") -config.substitutions.append(('%pointer_bounds_module', pointer_bounds_module)) -config.substitutions.append(('%/pointer_bounds_module', - '/'.join(os.path.normpath(pointer_bounds_module).split(os.sep)))) - back_deployment_runtime = lit_config.params.get('back_deployment_runtime', None) if back_deployment_runtime is not None: config.available_features.add('back_deployment_runtime') From ac3e684097eda7a587701249b882b49e6e221e21 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Mon, 28 Oct 2024 23:34:25 -0700 Subject: [PATCH 05/13] Handle FQTNs, always emit return even with Void-return type --- .../SwiftMacros/PointerBoundsMacro.swift | 66 +++++++++++-------- .../PointerBounds/CountedBy/CountExpr.swift | 4 +- .../CountedBy/MultipleParams.swift | 4 +- .../PointerBounds/CountedBy/Mutable.swift | 4 +- .../PointerBounds/CountedBy/MutableSpan.swift | 6 +- .../PointerBounds/CountedBy/Nullable.swift | 4 +- .../CountedBy/QualifiedTypes.swift | 24 +++++++ .../PointerBounds/CountedBy/Return.swift | 2 +- .../PointerBounds/CountedBy/SharedCount.swift | 4 +- .../PointerBounds/CountedBy/SimpleCount.swift | 4 +- .../PointerBounds/CountedBy/SimpleSpan.swift | 6 +- .../CountedBy/SimpleSpanWithReturn.swift | 2 +- .../PointerBounds/CountedBy/Unwrapped.swift | 4 +- .../MacroErrors/UnexpectedCountType.swift | 2 +- .../SizedBy/MultipleParams.swift | 4 +- .../PointerBounds/SizedBy/Mutable.swift | 4 +- .../SizedBy/MutableRawSpan.swift | 6 +- .../PointerBounds/SizedBy/Nullable.swift | 4 +- .../Macros/PointerBounds/SizedBy/Return.swift | 2 +- .../PointerBounds/SizedBy/SharedCount.swift | 4 +- .../PointerBounds/SizedBy/SimpleRawSpan.swift | 6 +- .../SizedBy/SimpleRawSpanWithReturn.swift | 2 +- .../PointerBounds/SizedBy/SimpleSize.swift | 4 +- .../PointerBounds/SizedBy/SizeExpr.swift | 4 +- .../PointerBounds/SizedBy/Unwrapped.swift | 4 +- 25 files changed, 107 insertions(+), 73 deletions(-) create mode 100644 test/Macros/PointerBounds/CountedBy/QualifiedTypes.swift diff --git a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift index fec640c0ec975..45bc3ad5641f2 100644 --- a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift @@ -93,6 +93,29 @@ enum UnsafePointerKind { case Mutable } +func getTypeName(_ type: TypeSyntax) throws -> TokenSyntax { + switch type.kind { + case .memberType: + let memberType = type.as(MemberTypeSyntax.self)! + if !memberType.baseType.isSwiftCoreModule { + throw DiagnosticError("expected pointer type in Swift core module, got type \(type) with base type \(memberType.baseType)", node: type) + } + return memberType.name + case .identifierType: + return type.as(IdentifierTypeSyntax.self)!.name + default: + throw DiagnosticError("expected pointer type, got \(type) with kind \(type.kind)", node: type) + } +} + +func replaceTypeName(_ type: TypeSyntax, _ name: TokenSyntax) -> TypeSyntax { + if let memberType = type.as(MemberTypeSyntax.self) { + return TypeSyntax(memberType.with(\.name, name)) + } + let idType = type.as(IdentifierTypeSyntax.self)! + return TypeSyntax(idType.with(\.name, name)) +} + func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) throws -> TypeSyntax { if let optType = prev.as(OptionalTypeSyntax.self) { return TypeSyntax(optType.with(\.wrappedType, try transformType(optType.wrappedType, variant, isSizedBy))) @@ -100,17 +123,15 @@ func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) th if let impOptType = prev.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) { return try transformType(impOptType.wrappedType, variant, isSizedBy) } - guard let idType = prev.as(IdentifierTypeSyntax.self) else { - throw DiagnosticError("expected pointer type, got \(prev) with kind \(prev.kind)", node: prev) - } - let text = idType.name.text + let name = try getTypeName(prev) + let text = name.text let kind: UnsafePointerKind = switch text { case "UnsafePointer": .Immutable case "UnsafeMutablePointer": .Mutable case "UnsafeRawPointer": .Immutable case "UnsafeMutableRawPointer": .Mutable default: throw DiagnosticError("expected Unsafe[Mutable][Raw]Pointer type for type \(prev)" + - " - first type token is '\(text)'", node: idType.name) + " - first type token is '\(text)'", node: name) } if isSizedBy { let token: TokenSyntax = switch (kind, variant.generateSpan) { @@ -122,7 +143,7 @@ func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) th return TypeSyntax(IdentifierTypeSyntax(name: token)) } if text == "UnsafeRawPointer" || text == "UnsafeMutableRawPointer" { - throw DiagnosticError("raw pointers only supported for SizedBy", node: idType.name) + throw DiagnosticError("raw pointers only supported for SizedBy", node: name) } let token: TokenSyntax = switch (kind, variant.generateSpan) { case (.Immutable, true): "Span" @@ -130,7 +151,7 @@ func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) th case (.Immutable, false): "UnsafeBufferPointer" case (.Mutable, false): "UnsafeMutableBufferPointer" } - return TypeSyntax(idType.with(\.name, token)) + return replaceTypeName(prev, token) } struct Variant { @@ -209,11 +230,6 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder { } } -func hasReturnType(_ signature: FunctionSignatureSyntax) -> Bool { - let returnType = signature.returnClause?.type.as(IdentifierTypeSyntax.self)?.name.text ?? "Void" - return returnType != "Void" -} - protocol PointerBoundsThunkBuilder: BoundsCheckedThunkBuilder { var name: TokenSyntax { get } var nullable: Bool { get } @@ -274,8 +290,7 @@ struct CountedOrSizedPointerThunkBuilder: PointerBoundsThunkBuilder { } func castIntToTargetType(expr: ExprSyntax, type: TypeSyntax) -> ExprSyntax { - let idType = type.as(IdentifierTypeSyntax.self)! - if idType.name.text == "Int" { + if type.isSwiftInt { return expr } return ExprSyntax("\(type)(exactly: \(expr))!") @@ -290,15 +305,10 @@ struct CountedOrSizedPointerThunkBuilder: PointerBoundsThunkBuilder { let call = try base.buildFunctionCall(args, variant) let ptrRef = unwrapIfNullable(ExprSyntax(DeclReferenceExprSyntax(baseName: name))) - let returnKw: String = if hasReturnType(signature) { - "return " - } else { - "" - } let funcName = isSizedBy ? "withUnsafeBytes" : "withUnsafeBufferPointer" let unwrappedCall = ExprSyntax(""" \(ptrRef).\(raw: funcName) { \(unwrappedName) in - \(raw: returnKw)\(call) + return \(call) } """) return unwrappedCall @@ -419,7 +429,7 @@ public struct PointerBoundsMacro: PeerMacro { guard let intLiteral = expr.as(IntegerLiteralExprSyntax.self) else { throw DiagnosticError("expected integer literal, got '\(expr)'", node: expr) } - guard let res = Int(intLiteral.literal.text) else { + guard let res = intLiteral.representedLiteralValue else { throw DiagnosticError("expected integer literal, got '\(expr)'", node: expr) } return res @@ -429,10 +439,14 @@ public struct PointerBoundsMacro: PeerMacro { guard let boolLiteral = expr.as(BooleanLiteralExprSyntax.self) else { throw DiagnosticError("expected boolean literal, got '\(expr)'", node: expr) } - guard let res = Bool(boolLiteral.literal.text) else { + switch boolLiteral.literal.tokenKind { + case .keyword(.true): + return true + case .keyword(.false): + return false + default: throw DiagnosticError("expected bool literal, got '\(expr)'", node: expr) } - return res } static func parseCountedByEnum(_ enumConstructorExpr: FunctionCallExprSyntax, _ signature: FunctionSignatureSyntax) throws -> ParamInfo { @@ -590,12 +604,8 @@ public struct PointerBoundsMacro: PeerMacro { CodeBlockItemSyntax(leadingTrivia: "\n", item: e) } } - let call = if hasReturnType(funcDecl.signature) { - CodeBlockItemSyntax(item: CodeBlockItemSyntax.Item(ReturnStmtSyntax(returnKeyword: .keyword(.return, trailingTrivia: " "), + let call = CodeBlockItemSyntax(item: CodeBlockItemSyntax.Item(ReturnStmtSyntax(returnKeyword: .keyword(.return, trailingTrivia: " "), expression: try builder.buildFunctionCall([:], variant)))) - } else { - CodeBlockItemSyntax(item: CodeBlockItemSyntax.Item(try builder.buildFunctionCall([:], variant))) - } let body = CodeBlockSyntax(statements: CodeBlockItemListSyntax(checks + [call])) let newFunc = funcDecl .with(\.signature, newSignature) diff --git a/test/Macros/PointerBounds/CountedBy/CountExpr.swift b/test/Macros/PointerBounds/CountedBy/CountExpr.swift index 23e6c92126c7e..cc14ba48d398d 100644 --- a/test/Macros/PointerBounds/CountedBy/CountExpr.swift +++ b/test/Macros/PointerBounds/CountedBy/CountExpr.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "size * count")) func myFunc(_ ptr: UnsafePointer, _ size: CInt, _ count: CInt) { @@ -13,6 +13,6 @@ func myFunc(_ ptr: UnsafePointer, _ size: CInt, _ count: CInt) { // CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { // CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") // CHECK-NEXT: } -// CHECK-NEXT: myFunc(ptr.baseAddress!, size, count) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, size, count) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/MultipleParams.swift b/test/Macros/PointerBounds/CountedBy/MultipleParams.swift index 10c363df3b74d..9f7bc53e8e98b 100644 --- a/test/Macros/PointerBounds/CountedBy/MultipleParams.swift +++ b/test/Macros/PointerBounds/CountedBy/MultipleParams.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len"), .countedBy(pointer: 3, count: "len2")) func myFunc(_ ptr: UnsafePointer, _ len: CInt, _ ptr2: UnsafePointer, _ len2: CInt) { @@ -9,5 +9,5 @@ func myFunc(_ ptr: UnsafePointer, _ len: CInt, _ ptr2: UnsafePointer // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer, _ ptr2: UnsafeBufferPointer) { -// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!, ptr2.baseAddress!, CInt(exactly: ptr2.count)!) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!, ptr2.baseAddress!, CInt(exactly: ptr2.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/Mutable.swift b/test/Macros/PointerBounds/CountedBy/Mutable.swift index 4d4f735d2a120..380b4836eb1af 100644 --- a/test/Macros/PointerBounds/CountedBy/Mutable.swift +++ b/test/Macros/PointerBounds/CountedBy/Mutable.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { @@ -9,6 +9,6 @@ func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeMutableBufferPointer) { -// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/MutableSpan.swift b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift index 8c2d0d237f5c5..37c637774a8ff 100644 --- a/test/Macros/PointerBounds/CountedBy/MutableSpan.swift +++ b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { @@ -9,8 +9,8 @@ func myFunc(_ ptr: UnsafeMutablePointer, _ len: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: MutableSpan) { -// CHECK-NEXT: ptr.withUnsafeBufferPointer { _ptrPtr in -// CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: return ptr.withUnsafeBufferPointer { _ptrPtr in +// CHECK-NEXT: return myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/Nullable.swift b/test/Macros/PointerBounds/CountedBy/Nullable.swift index 7c9a890ecbeaf..31f33ff26fddd 100644 --- a/test/Macros/PointerBounds/CountedBy/Nullable.swift +++ b/test/Macros/PointerBounds/CountedBy/Nullable.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafePointer?, _ len: CInt) { @@ -9,5 +9,5 @@ func myFunc(_ ptr: UnsafePointer?, _ len: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer?) { -// CHECK-NEXT: myFunc(ptr?.baseAddress, CInt(exactly: ptr?.count ?? 0)!) +// CHECK-NEXT: return myFunc(ptr?.baseAddress, CInt(exactly: ptr?.count ?? 0)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/QualifiedTypes.swift b/test/Macros/PointerBounds/CountedBy/QualifiedTypes.swift new file mode 100644 index 0000000000000..638f8d1229189 --- /dev/null +++ b/test/Macros/PointerBounds/CountedBy/QualifiedTypes.swift @@ -0,0 +1,24 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: pointer_bounds + +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s + +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func foo(_ ptr: Swift.UnsafePointer, _ len: Swift.Int) -> Swift.Void { +} + +@PointerBounds(.countedBy(pointer: 1, count: "len")) +func bar(_ ptr: Swift.UnsafePointer, _ len: Swift.Int) -> () { +} + +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func foo(_ ptr: Swift.UnsafeBufferPointer) -> Swift.Void { +// CHECK-NEXT: return foo(ptr.baseAddress!, ptr.count) +// CHECK-NEXT: } + +// CHECK: @_alwaysEmitIntoClient +// CHECK-NEXT: func bar(_ ptr: Swift.UnsafeBufferPointer) -> () { +// CHECK-NEXT: return bar(ptr.baseAddress!, ptr.count) +// CHECK-NEXT: } + + diff --git a/test/Macros/PointerBounds/CountedBy/Return.swift b/test/Macros/PointerBounds/CountedBy/Return.swift index 2b46ef663990d..c4612de8cd081 100644 --- a/test/Macros/PointerBounds/CountedBy/Return.swift +++ b/test/Macros/PointerBounds/CountedBy/Return.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafePointer, _ len: CInt) -> CInt { diff --git a/test/Macros/PointerBounds/CountedBy/SharedCount.swift b/test/Macros/PointerBounds/CountedBy/SharedCount.swift index 7e3019583a1d7..5e90c77b0188b 100644 --- a/test/Macros/PointerBounds/CountedBy/SharedCount.swift +++ b/test/Macros/PointerBounds/CountedBy/SharedCount.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len"), .countedBy(pointer: 2, count: "len")) func myFunc(_ ptr: UnsafePointer, _ ptr2: UnsafePointer, _ len: CInt) { @@ -17,5 +17,5 @@ func myFunc(_ ptr: UnsafePointer, _ ptr2: UnsafePointer, _ len: CInt // CHECK-NEXT: if ptr2.count < _ptr2Count || _ptr2Count < 0 { // CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") // CHECK-NEXT: } -// CHECK-NEXT: myFunc(ptr.baseAddress!, ptr2.baseAddress!, len) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, ptr2.baseAddress!, len) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleCount.swift b/test/Macros/PointerBounds/CountedBy/SimpleCount.swift index cca16399c35db..5c7794c941b70 100644 --- a/test/Macros/PointerBounds/CountedBy/SimpleCount.swift +++ b/test/Macros/PointerBounds/CountedBy/SimpleCount.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafePointer, _ len: CInt) { @@ -9,5 +9,5 @@ func myFunc(_ ptr: UnsafePointer, _ len: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer) { -// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift b/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift index 26393736e8ba4..d291c25d0e54d 100644 --- a/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift +++ b/test/Macros/PointerBounds/CountedBy/SimpleSpan.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafePointer, _ len: CInt) { @@ -9,7 +9,7 @@ func myFunc(_ ptr: UnsafePointer, _ len: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: Span) { -// CHECK-NEXT: ptr.withUnsafeBufferPointer { _ptrPtr in -// CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: return ptr.withUnsafeBufferPointer { _ptrPtr in +// CHECK-NEXT: return myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift b/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift index 5edc112828d46..26ccc76f05a41 100644 --- a/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift +++ b/test/Macros/PointerBounds/CountedBy/SimpleSpanWithReturn.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafePointer, _ len: CInt) -> CInt { diff --git a/test/Macros/PointerBounds/CountedBy/Unwrapped.swift b/test/Macros/PointerBounds/CountedBy/Unwrapped.swift index cb8617054c3b2..c25cc2ee0ea5c 100644 --- a/test/Macros/PointerBounds/CountedBy/Unwrapped.swift +++ b/test/Macros/PointerBounds/CountedBy/Unwrapped.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len")) func myFunc(_ ptr: UnsafePointer!, _ len: CInt) { @@ -9,6 +9,6 @@ func myFunc(_ ptr: UnsafePointer!, _ len: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeBufferPointer) { -// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift index 6b04771d6a5aa..bee40d6023fd1 100644 --- a/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift +++ b/test/Macros/PointerBounds/MacroErrors/UnexpectedCountType.swift @@ -3,7 +3,7 @@ // XFAIL: * -// RUN: not %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: not %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s // RUN: %target-typecheck-verify-swift %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -verify @PointerBounds(.countedBy(pointer: 1, count: "len")) diff --git a/test/Macros/PointerBounds/SizedBy/MultipleParams.swift b/test/Macros/PointerBounds/SizedBy/MultipleParams.swift index 23eebea0d2f11..951014bfa9332 100644 --- a/test/Macros/PointerBounds/SizedBy/MultipleParams.swift +++ b/test/Macros/PointerBounds/SizedBy/MultipleParams.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size"), .sizedBy(pointer: 3, size: "size2")) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ ptr2: UnsafeRawPointer, _ size2: CInt) { @@ -9,5 +9,5 @@ func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ ptr2: UnsafeRawPointer, _ s // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer, _ ptr2: UnsafeRawBufferPointer) { -// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!, ptr2.baseAddress!, CInt(exactly: ptr2.count)!) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!, ptr2.baseAddress!, CInt(exactly: ptr2.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Mutable.swift b/test/Macros/PointerBounds/SizedBy/Mutable.swift index cc21ae0758f84..5dae9d0128866 100644 --- a/test/Macros/PointerBounds/SizedBy/Mutable.swift +++ b/test/Macros/PointerBounds/SizedBy/Mutable.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size")) func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { @@ -9,5 +9,5 @@ func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeMutableRawBufferPointer) { -// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift index f517341a2a968..0be291e5ab985 100644 --- a/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift +++ b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { @@ -9,7 +9,7 @@ func myFunc(_ ptr: UnsafeMutableRawPointer, _ size: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: MutableRawSpan) { -// CHECK-NEXT: ptr.withUnsafeBytes { _ptrPtr in -// CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) +// CHECK-NEXT: return ptr.withUnsafeBytes { _ptrPtr in +// CHECK-NEXT: return myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) // CHECK-NEXT: } // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Nullable.swift b/test/Macros/PointerBounds/SizedBy/Nullable.swift index 6132fa5ed537d..1b78dd9d96b22 100644 --- a/test/Macros/PointerBounds/SizedBy/Nullable.swift +++ b/test/Macros/PointerBounds/SizedBy/Nullable.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size")) func myFunc(_ ptr: UnsafeRawPointer?, _ size: CInt) { @@ -9,5 +9,5 @@ func myFunc(_ ptr: UnsafeRawPointer?, _ size: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer?) { -// CHECK-NEXT: myFunc(ptr?.baseAddress, CInt(exactly: ptr?.count ?? 0)!) +// CHECK-NEXT: return myFunc(ptr?.baseAddress, CInt(exactly: ptr?.count ?? 0)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Return.swift b/test/Macros/PointerBounds/SizedBy/Return.swift index 5475733569bac..2f12e191de42c 100644 --- a/test/Macros/PointerBounds/SizedBy/Return.swift +++ b/test/Macros/PointerBounds/SizedBy/Return.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size")) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) -> CInt { diff --git a/test/Macros/PointerBounds/SizedBy/SharedCount.swift b/test/Macros/PointerBounds/SizedBy/SharedCount.swift index d8e79d05a15a5..8a56736116b3c 100644 --- a/test/Macros/PointerBounds/SizedBy/SharedCount.swift +++ b/test/Macros/PointerBounds/SizedBy/SharedCount.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size"), .sizedBy(pointer: 2, size: "size")) func myFunc(_ ptr: UnsafeRawPointer, _ ptr2: UnsafeRawPointer, _ size: CInt) { @@ -17,5 +17,5 @@ func myFunc(_ ptr: UnsafeRawPointer, _ ptr2: UnsafeRawPointer, _ size: CInt) { // CHECK-NEXT: if ptr2.count < _ptr2Count || _ptr2Count < 0 { // CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") // CHECK-NEXT: } -// CHECK-NEXT: myFunc(ptr.baseAddress!, ptr2.baseAddress!, size) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, ptr2.baseAddress!, size) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift b/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift index cc5951293661c..2cafac4e37c9c 100644 --- a/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift +++ b/test/Macros/PointerBounds/SizedBy/SimpleRawSpan.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { @@ -9,7 +9,7 @@ func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: RawSpan) { -// CHECK-NEXT: ptr.withUnsafeBytes { _ptrPtr in -// CHECK-NEXT: myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) +// CHECK-NEXT: return ptr.withUnsafeBytes { _ptrPtr in +// CHECK-NEXT: return myFunc(_ptrPtr.baseAddress!, CInt(exactly: ptr.byteCount)!) // CHECK-NEXT: } // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift b/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift index 8158026ae5d1f..d81971afc02b5 100644 --- a/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift +++ b/test/Macros/PointerBounds/SizedBy/SimpleRawSpanWithReturn.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1)) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) -> CInt { diff --git a/test/Macros/PointerBounds/SizedBy/SimpleSize.swift b/test/Macros/PointerBounds/SizedBy/SimpleSize.swift index 92eb44325739c..78745c8f76030 100644 --- a/test/Macros/PointerBounds/SizedBy/SimpleSize.swift +++ b/test/Macros/PointerBounds/SizedBy/SimpleSize.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size")) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { @@ -9,5 +9,5 @@ func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer) { -// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/SizeExpr.swift b/test/Macros/PointerBounds/SizedBy/SizeExpr.swift index c73f486c94b29..db8d93d87366d 100644 --- a/test/Macros/PointerBounds/SizedBy/SizeExpr.swift +++ b/test/Macros/PointerBounds/SizedBy/SizeExpr.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size * count")) func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ count: CInt) { @@ -13,5 +13,5 @@ func myFunc(_ ptr: UnsafeRawPointer, _ size: CInt, _ count: CInt) { // CHECK-NEXT: if ptr.count < _ptrCount || _ptrCount < 0 { // CHECK-NEXT: fatalError("bounds check failure when calling unsafe function") // CHECK-NEXT: } -// CHECK-NEXT: myFunc(ptr.baseAddress!, size, count) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, size, count) // CHECK-NEXT: } diff --git a/test/Macros/PointerBounds/SizedBy/Unwrapped.swift b/test/Macros/PointerBounds/SizedBy/Unwrapped.swift index e78e6677b040f..1402e81e731e9 100644 --- a/test/Macros/PointerBounds/SizedBy/Unwrapped.swift +++ b/test/Macros/PointerBounds/SizedBy/Unwrapped.swift @@ -1,7 +1,7 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds -// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "len")) func myFunc(_ ptr: UnsafeRawPointer!, _ len: CInt) { @@ -9,6 +9,6 @@ func myFunc(_ ptr: UnsafeRawPointer!, _ len: CInt) { // CHECK: @_alwaysEmitIntoClient // CHECK-NEXT: func myFunc(_ ptr: UnsafeRawBufferPointer) { -// CHECK-NEXT: myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) +// CHECK-NEXT: return myFunc(ptr.baseAddress!, CInt(exactly: ptr.count)!) // CHECK-NEXT: } From b3f4801560273f71040c86eb720217e0f7e78726 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Mon, 28 Oct 2024 23:51:01 -0700 Subject: [PATCH 06/13] run swift-format --- .../SwiftMacros/PointerBoundsMacro.swift | 1164 +++++++++-------- 1 file changed, 626 insertions(+), 538 deletions(-) diff --git a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift index 45bc3ad5641f2..106c5a25a43b2 100644 --- a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift @@ -5,411 +5,452 @@ import SwiftSyntaxBuilder import SwiftSyntaxMacros protocol ParamInfo: CustomStringConvertible { - var description: String { get } - var original: ExprSyntax { get } - var pointerIndex: Int { get } - var nonescaping: Bool { get set } - - func getBoundsCheckedThunkBuilder(_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax, - _ variant: Variant - ) -> BoundsCheckedThunkBuilder + var description: String { get } + var original: ExprSyntax { get } + var pointerIndex: Int { get } + var nonescaping: Bool { get set } + + func getBoundsCheckedThunkBuilder( + _ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax, + _ variant: Variant + ) -> BoundsCheckedThunkBuilder } struct CountedBy: ParamInfo { - var pointerIndex: Int - var count: ExprSyntax - var sizedBy: Bool - var nonescaping: Bool - var original: ExprSyntax - - var description: String { - if sizedBy { - return ".sizedBy(pointer: \(pointerIndex), size: \"\(count)\", nonescaping: \(nonescaping))" - } - return ".countedBy(pointer: \(pointerIndex), count: \"\(count)\", nonescaping: \(nonescaping))" - } - - func getBoundsCheckedThunkBuilder(_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax, - _ variant: Variant - ) -> BoundsCheckedThunkBuilder { + var pointerIndex: Int + var count: ExprSyntax + var sizedBy: Bool + var nonescaping: Bool + var original: ExprSyntax + + var description: String { + if sizedBy { + return ".sizedBy(pointer: \(pointerIndex), size: \"\(count)\", nonescaping: \(nonescaping))" + } + return ".countedBy(pointer: \(pointerIndex), count: \"\(count)\", nonescaping: \(nonescaping))" + } + + func getBoundsCheckedThunkBuilder( + _ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax, + _ variant: Variant + ) -> BoundsCheckedThunkBuilder { let funcParam = getParam(funcDecl, pointerIndex - 1) let paramName = funcParam.secondName ?? funcParam.firstName let isNullable = funcParam.type.is(OptionalTypeSyntax.self) - return CountedOrSizedPointerThunkBuilder(base: base, index: pointerIndex - 1, countExpr: count, - name: paramName, nullable: isNullable, signature: funcDecl.signature, nonescaping: nonescaping, isSizedBy: sizedBy) - } + return CountedOrSizedPointerThunkBuilder( + base: base, index: pointerIndex - 1, countExpr: count, + name: paramName, nullable: isNullable, signature: funcDecl.signature, + nonescaping: nonescaping, isSizedBy: sizedBy) + } } struct EndedBy: ParamInfo { - var pointerIndex: Int - var endIndex: Int - var nonescaping: Bool - var original: ExprSyntax - - var description: String { - return ".endedBy(start: \(pointerIndex), end: \(endIndex), nonescaping: \(nonescaping))" - } - - func getBoundsCheckedThunkBuilder(_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax, - _ variant: Variant - ) -> BoundsCheckedThunkBuilder { + var pointerIndex: Int + var endIndex: Int + var nonescaping: Bool + var original: ExprSyntax + + var description: String { + return ".endedBy(start: \(pointerIndex), end: \(endIndex), nonescaping: \(nonescaping))" + } + + func getBoundsCheckedThunkBuilder( + _ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax, + _ variant: Variant + ) -> BoundsCheckedThunkBuilder { let funcParam = getParam(funcDecl, pointerIndex - 1) let paramName = funcParam.secondName ?? funcParam.firstName let isNullable = funcParam.type.is(OptionalTypeSyntax.self) - return EndedByPointerThunkBuilder(base: base, startIndex: pointerIndex - 1, endIndex: endIndex - 1, - name: paramName, nullable: isNullable, signature: funcDecl.signature, nonescaping: nonescaping) - } + return EndedByPointerThunkBuilder( + base: base, startIndex: pointerIndex - 1, endIndex: endIndex - 1, + name: paramName, nullable: isNullable, signature: funcDecl.signature, nonescaping: nonescaping + ) + } } struct RuntimeError: Error { - let description: String + let description: String - init(_ description: String) { - self.description = description - } + init(_ description: String) { + self.description = description + } - var errorDescription: String? { - description - } + var errorDescription: String? { + description + } } struct DiagnosticError: Error { - let description: String - let node: SyntaxProtocol - let notes: [Note] - - init(_ description: String, node: SyntaxProtocol, notes: [Note] = []) { - self.description = description - self.node = node - self.notes = notes - } - - var errorDescription: String? { - description - } + let description: String + let node: SyntaxProtocol + let notes: [Note] + + init(_ description: String, node: SyntaxProtocol, notes: [Note] = []) { + self.description = description + self.node = node + self.notes = notes + } + + var errorDescription: String? { + description + } } enum UnsafePointerKind { - case Immutable - case Mutable + case Immutable + case Mutable } func getTypeName(_ type: TypeSyntax) throws -> TokenSyntax { - switch type.kind { - case .memberType: - let memberType = type.as(MemberTypeSyntax.self)! - if !memberType.baseType.isSwiftCoreModule { - throw DiagnosticError("expected pointer type in Swift core module, got type \(type) with base type \(memberType.baseType)", node: type) - } - return memberType.name - case .identifierType: - return type.as(IdentifierTypeSyntax.self)!.name - default: - throw DiagnosticError("expected pointer type, got \(type) with kind \(type.kind)", node: type) - } + switch type.kind { + case .memberType: + let memberType = type.as(MemberTypeSyntax.self)! + if !memberType.baseType.isSwiftCoreModule { + throw DiagnosticError( + "expected pointer type in Swift core module, got type \(type) with base type \(memberType.baseType)", + node: type) + } + return memberType.name + case .identifierType: + return type.as(IdentifierTypeSyntax.self)!.name + default: + throw DiagnosticError("expected pointer type, got \(type) with kind \(type.kind)", node: type) + } } func replaceTypeName(_ type: TypeSyntax, _ name: TokenSyntax) -> TypeSyntax { - if let memberType = type.as(MemberTypeSyntax.self) { - return TypeSyntax(memberType.with(\.name, name)) - } - let idType = type.as(IdentifierTypeSyntax.self)! - return TypeSyntax(idType.with(\.name, name)) + if let memberType = type.as(MemberTypeSyntax.self) { + return TypeSyntax(memberType.with(\.name, name)) + } + let idType = type.as(IdentifierTypeSyntax.self)! + return TypeSyntax(idType.with(\.name, name)) } func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) throws -> TypeSyntax { - if let optType = prev.as(OptionalTypeSyntax.self) { - return TypeSyntax(optType.with(\.wrappedType, try transformType(optType.wrappedType, variant, isSizedBy))) - } - if let impOptType = prev.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) { - return try transformType(impOptType.wrappedType, variant, isSizedBy) - } - let name = try getTypeName(prev) - let text = name.text - let kind: UnsafePointerKind = switch text { - case "UnsafePointer": .Immutable - case "UnsafeMutablePointer": .Mutable - case "UnsafeRawPointer": .Immutable + if let optType = prev.as(OptionalTypeSyntax.self) { + return TypeSyntax( + optType.with(\.wrappedType, try transformType(optType.wrappedType, variant, isSizedBy))) + } + if let impOptType = prev.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) { + return try transformType(impOptType.wrappedType, variant, isSizedBy) + } + let name = try getTypeName(prev) + let text = name.text + let kind: UnsafePointerKind = + switch text { + case "UnsafePointer": .Immutable + case "UnsafeMutablePointer": .Mutable + case "UnsafeRawPointer": .Immutable case "UnsafeMutableRawPointer": .Mutable - default: throw DiagnosticError("expected Unsafe[Mutable][Raw]Pointer type for type \(prev)" + - " - first type token is '\(text)'", node: name) - } - if isSizedBy { - let token: TokenSyntax = switch (kind, variant.generateSpan) { - case (.Immutable, true): "RawSpan" - case (.Mutable, true): "MutableRawSpan" - case (.Immutable, false): "UnsafeRawBufferPointer" - case (.Mutable, false): "UnsafeMutableRawBufferPointer" - } - return TypeSyntax(IdentifierTypeSyntax(name: token)) - } - if text == "UnsafeRawPointer" || text == "UnsafeMutableRawPointer" { - throw DiagnosticError("raw pointers only supported for SizedBy", node: name) - } - let token: TokenSyntax = switch (kind, variant.generateSpan) { - case (.Immutable, true): "Span" - case (.Mutable, true): "MutableSpan" + default: + throw DiagnosticError( + "expected Unsafe[Mutable][Raw]Pointer type for type \(prev)" + + " - first type token is '\(text)'", node: name) + } + if isSizedBy { + let token: TokenSyntax = + switch (kind, variant.generateSpan) { + case (.Immutable, true): "RawSpan" + case (.Mutable, true): "MutableRawSpan" + case (.Immutable, false): "UnsafeRawBufferPointer" + case (.Mutable, false): "UnsafeMutableRawBufferPointer" + } + return TypeSyntax(IdentifierTypeSyntax(name: token)) + } + if text == "UnsafeRawPointer" || text == "UnsafeMutableRawPointer" { + throw DiagnosticError("raw pointers only supported for SizedBy", node: name) + } + let token: TokenSyntax = + switch (kind, variant.generateSpan) { + case (.Immutable, true): "Span" + case (.Mutable, true): "MutableSpan" case (.Immutable, false): "UnsafeBufferPointer" - case (.Mutable, false): "UnsafeMutableBufferPointer" + case (.Mutable, false): "UnsafeMutableBufferPointer" } - return replaceTypeName(prev, token) + return replaceTypeName(prev, token) } struct Variant { - public let generateSpan: Bool - public let skipTrivialCount: Bool + public let generateSpan: Bool + public let skipTrivialCount: Bool } protocol BoundsCheckedThunkBuilder { - func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax - func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] - func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws -> FunctionSignatureSyntax + func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax + func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] + func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws + -> FunctionSignatureSyntax } func getParam(_ signature: FunctionSignatureSyntax, _ paramIndex: Int) -> FunctionParameterSyntax { - let params = signature.parameterClause.parameters - let index = if paramIndex > 0 { - params.index(params.startIndex, offsetBy: paramIndex) + let params = signature.parameterClause.parameters + let index = + if paramIndex > 0 { + params.index(params.startIndex, offsetBy: paramIndex) } else { - params.startIndex + params.startIndex } - return params[index] + return params[index] } func getParam(_ funcDecl: FunctionDeclSyntax, _ paramIndex: Int) -> FunctionParameterSyntax { - return getParam(funcDecl.signature, paramIndex) + return getParam(funcDecl.signature, paramIndex) } struct FunctionCallBuilder: BoundsCheckedThunkBuilder { - let base: FunctionDeclSyntax - init(_ function: FunctionDeclSyntax) { - base = function - } - - func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] { - return [] - } - - func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws -> FunctionSignatureSyntax { - var newParams = base.signature.parameterClause.parameters.enumerated().filter { - let type = argTypes[$0.offset] - // 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 last = newParams.popLast()! - newParams.append(last.with(\.trailingComma, nil)) - - return base.signature.with(\.parameterClause.parameters, FunctionParameterListSyntax(newParams)) - } - - func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax], _: Variant) throws -> ExprSyntax { - 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 declref = DeclReferenceExprSyntax(baseName: name) - return pointerArgs[i] ?? ExprSyntax(declref) - } - let labels: [TokenSyntax?] = base.signature.parameterClause.parameters.map { param in - let firstName = param.firstName - if firstName.trimmed.text == "_" { - return nil - } - return firstName - } - let labeledArgs: [LabeledExprSyntax] = zip(labels, args).enumerated().map { (i, e) in - let (label, arg) = e - var comma: TokenSyntax? = nil - if i < args.count - 1 { - comma = .commaToken() - } - return LabeledExprSyntax(label: label, expression: arg, trailingComma: comma) - } - return ExprSyntax(FunctionCallExprSyntax(calledExpression: functionRef, leftParen: .leftParenToken(), - arguments: LabeledExprListSyntax(labeledArgs), rightParen: .rightParenToken())) - } + let base: FunctionDeclSyntax + init(_ function: FunctionDeclSyntax) { + base = function + } + + func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] { + return [] + } + + func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws + -> FunctionSignatureSyntax + { + var newParams = base.signature.parameterClause.parameters.enumerated().filter { + let type = argTypes[$0.offset] + // 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 last = newParams.popLast()! + newParams.append(last.with(\.trailingComma, nil)) + + return base.signature.with(\.parameterClause.parameters, FunctionParameterListSyntax(newParams)) + } + + func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax], _: Variant) throws -> ExprSyntax { + 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 declref = DeclReferenceExprSyntax(baseName: name) + return pointerArgs[i] ?? ExprSyntax(declref) + } + let labels: [TokenSyntax?] = base.signature.parameterClause.parameters.map { param in + let firstName = param.firstName + if firstName.trimmed.text == "_" { + return nil + } + return firstName + } + let labeledArgs: [LabeledExprSyntax] = zip(labels, args).enumerated().map { (i, e) in + let (label, arg) = e + var comma: TokenSyntax? = nil + if i < args.count - 1 { + comma = .commaToken() + } + return LabeledExprSyntax(label: label, expression: arg, trailingComma: comma) + } + return ExprSyntax( + FunctionCallExprSyntax( + calledExpression: functionRef, leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax(labeledArgs), rightParen: .rightParenToken())) + } } protocol PointerBoundsThunkBuilder: BoundsCheckedThunkBuilder { - var name: TokenSyntax { get } - var nullable: Bool { get } - var signature: FunctionSignatureSyntax { get } - var nonescaping: Bool { get } + var name: TokenSyntax { get } + var nullable: Bool { get } + var signature: FunctionSignatureSyntax { get } + var nonescaping: Bool { get } } struct CountedOrSizedPointerThunkBuilder: PointerBoundsThunkBuilder { - public let base: BoundsCheckedThunkBuilder - public let index: Int - public let countExpr: ExprSyntax - public let name: TokenSyntax - public let nullable: Bool - public let signature: FunctionSignatureSyntax - public let nonescaping: Bool - public let isSizedBy: Bool - - func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws -> FunctionSignatureSyntax { - var types = argTypes - let param = getParam(signature, index) - types[index] = try transformType(param.type, variant, isSizedBy) - if variant.skipTrivialCount { - if let countVar = countExpr.as(DeclReferenceExprSyntax.self) { - let i = try getParameterIndexForDeclRef(signature.parameterClause.parameters, countVar) - types[i] = nil as TypeSyntax? - } - } - return try base.buildFunctionSignature(types, variant) - } - - func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] { - var res = try base.buildBoundsChecks(variant) - let countName: TokenSyntax = "_\(raw: name)Count" - let count: VariableDeclSyntax = try VariableDeclSyntax("let \(countName): some BinaryInteger = \(countExpr)") - res.append(CodeBlockItemSyntax.Item(count)) - - let countCheck = ExprSyntax(""" + public let base: BoundsCheckedThunkBuilder + public let index: Int + public let countExpr: ExprSyntax + public let name: TokenSyntax + public let nullable: Bool + public let signature: FunctionSignatureSyntax + public let nonescaping: Bool + public let isSizedBy: Bool + + func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws + -> FunctionSignatureSyntax + { + var types = argTypes + let param = getParam(signature, index) + types[index] = try transformType(param.type, variant, isSizedBy) + if variant.skipTrivialCount { + if let countVar = countExpr.as(DeclReferenceExprSyntax.self) { + let i = try getParameterIndexForDeclRef(signature.parameterClause.parameters, countVar) + types[i] = nil as TypeSyntax? + } + } + return try base.buildFunctionSignature(types, variant) + } + + func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] { + var res = try base.buildBoundsChecks(variant) + let countName: TokenSyntax = "_\(raw: name)Count" + let count: VariableDeclSyntax = try VariableDeclSyntax( + "let \(countName): some BinaryInteger = \(countExpr)") + res.append(CodeBlockItemSyntax.Item(count)) + + let countCheck = ExprSyntax( + """ if \(getCount(variant)) < \(countName) || \(countName) < 0 { - fatalError("bounds check failure when calling unsafe function") - } - """) - res.append(CodeBlockItemSyntax.Item(countCheck)) - return res - } - - func unwrapIfNullable(_ expr: ExprSyntax) -> ExprSyntax { - if nullable { - return ExprSyntax(ForceUnwrapExprSyntax(expression: expr)) - } - return expr - } - - func unwrapIfNonnullable(_ expr: ExprSyntax) -> ExprSyntax { - if !nullable { - return ExprSyntax(ForceUnwrapExprSyntax(expression: expr)) - } - return expr - } - - func castIntToTargetType(expr: ExprSyntax, type: TypeSyntax) -> ExprSyntax { - if type.isSwiftInt { - return expr - } - return ExprSyntax("\(type)(exactly: \(expr))!") - } - - func buildUnwrapCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax { - let unwrappedName = TokenSyntax("_\(name)Ptr") - var args = argOverrides - let argExpr = ExprSyntax("\(unwrappedName).baseAddress") - assert(args[index] == nil) - args[index] = unwrapIfNonnullable(argExpr) - let call = try base.buildFunctionCall(args, variant) - let ptrRef = unwrapIfNullable(ExprSyntax(DeclReferenceExprSyntax(baseName: name))) - - let funcName = isSizedBy ? "withUnsafeBytes" : "withUnsafeBufferPointer" - let unwrappedCall = ExprSyntax(""" + fatalError("bounds check failure when calling unsafe function") + } + """) + res.append(CodeBlockItemSyntax.Item(countCheck)) + return res + } + + func unwrapIfNullable(_ expr: ExprSyntax) -> ExprSyntax { + if nullable { + return ExprSyntax(ForceUnwrapExprSyntax(expression: expr)) + } + return expr + } + + func unwrapIfNonnullable(_ expr: ExprSyntax) -> ExprSyntax { + if !nullable { + return ExprSyntax(ForceUnwrapExprSyntax(expression: expr)) + } + return expr + } + + func castIntToTargetType(expr: ExprSyntax, type: TypeSyntax) -> ExprSyntax { + if type.isSwiftInt { + return expr + } + return ExprSyntax("\(type)(exactly: \(expr))!") + } + + func buildUnwrapCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax { + let unwrappedName = TokenSyntax("_\(name)Ptr") + var args = argOverrides + let argExpr = ExprSyntax("\(unwrappedName).baseAddress") + assert(args[index] == nil) + args[index] = unwrapIfNonnullable(argExpr) + let call = try base.buildFunctionCall(args, variant) + let ptrRef = unwrapIfNullable(ExprSyntax(DeclReferenceExprSyntax(baseName: name))) + + let funcName = isSizedBy ? "withUnsafeBytes" : "withUnsafeBufferPointer" + let unwrappedCall = ExprSyntax( + """ \(ptrRef).\(raw: funcName) { \(unwrappedName) in - return \(call) - } - """) - return unwrappedCall - } - - func getCount(_ variant: Variant) -> ExprSyntax { - let countName = isSizedBy && variant.generateSpan ? "byteCount" : "count" - return if nullable { - ExprSyntax("\(name)?.\(raw: countName) ?? 0") - } else { - ExprSyntax("\(name).\(raw: countName)") + return \(call) } - } + """) + return unwrappedCall + } - func getPointerArg() -> ExprSyntax { - return if nullable { - ExprSyntax("\(name)?.baseAddress") - } else { - ExprSyntax("\(name).baseAddress!") - } + func getCount(_ variant: Variant) -> ExprSyntax { + let countName = isSizedBy && variant.generateSpan ? "byteCount" : "count" + return if nullable { + ExprSyntax("\(name)?.\(raw: countName) ?? 0") + } else { + ExprSyntax("\(name).\(raw: countName)") } + } - func buildFunctionCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax { - var args = argOverrides - if variant.skipTrivialCount { - assert(countExpr.is(DeclReferenceExprSyntax.self) || countExpr.is(IntegerLiteralExprSyntax.self)) - if let countVar = countExpr.as(DeclReferenceExprSyntax.self) { - let i = try getParameterIndexForDeclRef(signature.parameterClause.parameters, countVar) - assert(args[i] == nil) - args[i] = castIntToTargetType(expr: getCount(variant), type: getParam(signature, i).type) - } - } - assert(args[index] == nil) - if variant.generateSpan { - assert(nonescaping) - let unwrappedCall = try buildUnwrapCall(args, variant) - if nullable { - var nullArgs = args - nullArgs[index] = ExprSyntax(NilLiteralExprSyntax(nilKeyword: .keyword(.nil))) - return ExprSyntax(""" - if \(name) == nil { - \(try base.buildFunctionCall(nullArgs, variant)) - } else { - \(unwrappedCall) - } - """) + func getPointerArg() -> ExprSyntax { + return if nullable { + ExprSyntax("\(name)?.baseAddress") + } else { + ExprSyntax("\(name).baseAddress!") + } + } + + func buildFunctionCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax + { + var args = argOverrides + if variant.skipTrivialCount { + assert( + countExpr.is(DeclReferenceExprSyntax.self) || countExpr.is(IntegerLiteralExprSyntax.self)) + if let countVar = countExpr.as(DeclReferenceExprSyntax.self) { + let i = try getParameterIndexForDeclRef(signature.parameterClause.parameters, countVar) + assert(args[i] == nil) + args[i] = castIntToTargetType(expr: getCount(variant), type: getParam(signature, i).type) + } + } + assert(args[index] == nil) + if variant.generateSpan { + assert(nonescaping) + let unwrappedCall = try buildUnwrapCall(args, variant) + if nullable { + var nullArgs = args + nullArgs[index] = ExprSyntax(NilLiteralExprSyntax(nilKeyword: .keyword(.nil))) + return ExprSyntax( + """ + if \(name) == nil { + \(try base.buildFunctionCall(nullArgs, variant)) + } else { + \(unwrappedCall) } - return unwrappedCall - } - - args[index] = getPointerArg() - return try base.buildFunctionCall(args, variant) + """) + } + return unwrappedCall } + + args[index] = getPointerArg() + return try base.buildFunctionCall(args, variant) + } } struct EndedByPointerThunkBuilder: PointerBoundsThunkBuilder { - public let base: BoundsCheckedThunkBuilder - public let startIndex: Int - public let endIndex: Int - public let name: TokenSyntax - public let nullable: Bool - public let signature: FunctionSignatureSyntax - public let nonescaping: Bool - - func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws -> FunctionSignatureSyntax { - throw RuntimeError("endedBy support not yet implemented") - } - - func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] { - throw RuntimeError("endedBy support not yet implemented") - } - - func buildFunctionCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax { - throw RuntimeError("endedBy support not yet implemented") - } + public let base: BoundsCheckedThunkBuilder + public let startIndex: Int + public let endIndex: Int + public let name: TokenSyntax + public let nullable: Bool + public let signature: FunctionSignatureSyntax + public let nonescaping: Bool + + func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ variant: Variant) throws + -> FunctionSignatureSyntax + { + throw RuntimeError("endedBy support not yet implemented") + } + + func buildBoundsChecks(_ variant: Variant) throws -> [CodeBlockItemSyntax.Item] { + throw RuntimeError("endedBy support not yet implemented") + } + + func buildFunctionCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax + { + throw RuntimeError("endedBy support not yet implemented") + } } func getArgumentByName(_ argumentList: LabeledExprListSyntax, _ name: String) throws -> ExprSyntax { - guard let arg = argumentList.first(where: { - return $0.label?.text == name - }) else { - throw DiagnosticError("no argument with name '\(name)' in '\(argumentList)'", node: argumentList) - } - return arg.expression + guard + let arg = argumentList.first(where: { + return $0.label?.text == name + }) + else { + throw DiagnosticError( + "no argument with name '\(name)' in '\(argumentList)'", node: argumentList) + } + return arg.expression } -func getOptionalArgumentByName(_ argumentList: LabeledExprListSyntax, _ name: String) -> ExprSyntax? { - return argumentList.first(where: { - $0.label?.text == name - })?.expression +func getOptionalArgumentByName(_ argumentList: LabeledExprListSyntax, _ name: String) -> ExprSyntax? +{ + return argumentList.first(where: { + $0.label?.text == name + })?.expression } -func getParameterIndexForDeclRef(_ parameterList: FunctionParameterListSyntax, _ ref: DeclReferenceExprSyntax) throws -> Int { - let name = ref.baseName.text - guard let index = parameterList.enumerated().first(where: { (_: Int, param: FunctionParameterSyntax) in - let paramenterName = param.secondName ?? param.firstName - return paramenterName.trimmed.text == name - })?.offset else { - throw DiagnosticError("no parameter with name '\(name)' in '\(parameterList)'", node: ref) - } - return index +func getParameterIndexForDeclRef( + _ parameterList: FunctionParameterListSyntax, _ ref: DeclReferenceExprSyntax +) throws -> Int { + let name = ref.baseName.text + guard + let index = parameterList.enumerated().first(where: { + (_: Int, param: FunctionParameterSyntax) in + let paramenterName = param.secondName ?? param.firstName + return paramenterName.trimmed.text == name + })?.offset + else { + throw DiagnosticError("no parameter with name '\(name)' in '\(parameterList)'", node: ref) + } + return index } /// A macro that adds safe(r) wrappers for functions with unsafe pointer types. @@ -418,214 +459,261 @@ func getParameterIndexForDeclRef(_ parameterList: FunctionParameterListSyntax, _ /// for automatic application by ClangImporter when the C declaration is annotated /// appropriately. public struct PointerBoundsMacro: PeerMacro { - static func parseEnumName(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> String { - guard let calledExpr = enumConstructorExpr.calledExpression.as(MemberAccessExprSyntax.self) else { - throw DiagnosticError("expected PointerParam enum literal as argument, got '\(enumConstructorExpr)'", node: enumConstructorExpr) - } - return calledExpr.declName.baseName.text - } - - static func getIntLiteralValue(_ expr: ExprSyntax) throws -> Int { - guard let intLiteral = expr.as(IntegerLiteralExprSyntax.self) else { - throw DiagnosticError("expected integer literal, got '\(expr)'", node: expr) - } - guard let res = intLiteral.representedLiteralValue else { - throw DiagnosticError("expected integer literal, got '\(expr)'", node: expr) - } - return res - } - - static func getBoolLiteralValue(_ expr: ExprSyntax) throws -> Bool { - guard let boolLiteral = expr.as(BooleanLiteralExprSyntax.self) else { - throw DiagnosticError("expected boolean literal, got '\(expr)'", node: expr) - } - switch boolLiteral.literal.tokenKind { - case .keyword(.true): - return true - case .keyword(.false): - return false - default: - throw DiagnosticError("expected bool literal, got '\(expr)'", node: expr) - } - } - - static func parseCountedByEnum(_ enumConstructorExpr: FunctionCallExprSyntax, _ signature: FunctionSignatureSyntax) throws -> ParamInfo { - let argumentList = enumConstructorExpr.arguments - let pointerParamIndexArg = try getArgumentByName(argumentList, "pointer") - let pointerParamIndex: Int = try getIntLiteralValue(pointerParamIndexArg) - let countExprArg = try getArgumentByName(argumentList, "count") - guard let countExprStringLit = countExprArg.as(StringLiteralExprSyntax.self) else { - throw DiagnosticError("expected string literal for 'count' parameter, got \(countExprArg)", node: countExprArg) - } - let unwrappedCountExpr = ExprSyntax(stringLiteral: countExprStringLit.representedLiteralValue!) - if let countVar = unwrappedCountExpr.as(DeclReferenceExprSyntax.self) { - // Perform this lookup here so we can override the position to point to the string literal - // instead of line 1, column 1 - do { - _ = try getParameterIndexForDeclRef(signature.parameterClause.parameters, countVar) - } catch let error as DiagnosticError { - throw DiagnosticError(error.description, node: countExprStringLit, notes: error.notes) - } - } - return CountedBy(pointerIndex: pointerParamIndex, count: unwrappedCountExpr, sizedBy: false, nonescaping: false, original: ExprSyntax(enumConstructorExpr)) - } - - static func parseSizedByEnum(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> ParamInfo { - let argumentList = enumConstructorExpr.arguments - let pointerParamIndexArg = try getArgumentByName(argumentList, "pointer") - let pointerParamIndex: Int = try getIntLiteralValue(pointerParamIndexArg) - let sizeExprArg = try getArgumentByName(argumentList, "size") - guard let sizeExprStringLit = sizeExprArg.as(StringLiteralExprSyntax.self) else { - throw DiagnosticError("expected string literal for 'size' parameter, got \(sizeExprArg)", node: sizeExprArg) - } - let unwrappedCountExpr = ExprSyntax(stringLiteral: sizeExprStringLit.representedLiteralValue!) - return CountedBy(pointerIndex: pointerParamIndex, count: unwrappedCountExpr, sizedBy: true, nonescaping: false, original: ExprSyntax(enumConstructorExpr)) - } - - static func parseEndedByEnum(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> ParamInfo { - let argumentList = enumConstructorExpr.arguments - let startParamIndexArg = try getArgumentByName(argumentList, "start") - let startParamIndex: Int = try getIntLiteralValue(startParamIndexArg) - let endParamIndexArg = try getArgumentByName(argumentList, "end") - let endParamIndex: Int = try getIntLiteralValue(endParamIndexArg) - let nonescapingExprArg = getOptionalArgumentByName(argumentList, "nonescaping") - let nonescaping = if nonescapingExprArg != nil { - try getBoolLiteralValue(nonescapingExprArg!) - } else { - false - } - return EndedBy(pointerIndex: startParamIndex, endIndex: endParamIndex, nonescaping: nonescaping, original: ExprSyntax(enumConstructorExpr)) - } - - static func parseNonEscaping(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> Int { - let argumentList = enumConstructorExpr.arguments - let pointerParamIndexArg = try getArgumentByName(argumentList, "pointer") - let pointerParamIndex: Int = try getIntLiteralValue(pointerParamIndexArg) - return pointerParamIndex - } - - static func parseMacroParam(_ paramAST: LabeledExprSyntax, _ signature: FunctionSignatureSyntax, nonescapingPointers: inout Set) throws -> ParamInfo? { - let paramExpr = paramAST.expression - guard let enumConstructorExpr = paramExpr.as(FunctionCallExprSyntax.self) else { - throw DiagnosticError("expected PointerParam enum literal as argument, got '\(paramExpr)'", node: paramExpr) - } - let enumName = try parseEnumName(enumConstructorExpr) - switch enumName { - case "countedBy": return try parseCountedByEnum(enumConstructorExpr, signature) - case "sizedBy": return try parseSizedByEnum(enumConstructorExpr) - case "endedBy": return try parseEndedByEnum(enumConstructorExpr) - case "nonescaping": - let index = try parseNonEscaping(enumConstructorExpr) - nonescapingPointers.insert(index) - return nil - default: throw DiagnosticError("expected 'countedBy', 'sizedBy', 'endedBy' or 'nonescaping', got '\(enumName)'", node: enumConstructorExpr) - } - } - - static func hasSafeVariants(_ parsedArgs: [ParamInfo]) -> Bool { - return parsedArgs.contains { $0.nonescaping } - } - - static func hasTrivialCountVariants(_ parsedArgs: [ParamInfo]) -> Bool { - let countExprs = parsedArgs.compactMap { switch $0 { - case let c as CountedBy: return c.count - default: return nil - }} - let trivialCounts = countExprs.filter { - $0.is(DeclReferenceExprSyntax.self) || $0 .is(IntegerLiteralExprSyntax.self) - } - // don't generate trivial count variants if there are any non-trivial counts - if trivialCounts.count < countExprs.count { - return false - } - let countVars = trivialCounts.filter { $0.is(DeclReferenceExprSyntax.self) } - let distinctCountVars = Set(countVars.map { - return $0.as(DeclReferenceExprSyntax.self)!.baseName.text - }) - // don't generate trivial count variants if two count expressions refer to the same parameter - return countVars.count == distinctCountVars.count - } - - static func checkArgs(_ args: [ParamInfo], _ funcDecl: FunctionDeclSyntax) throws { - var argByIndex: [Int: ParamInfo] = [:] - let paramCount = funcDecl.signature.parameterClause.parameters.count - try args.forEach { pointerArg in - let i = pointerArg.pointerIndex - if i < 1 || i > paramCount { - let noteMessage = if paramCount > 0 { - "function \(funcDecl.name) has parameter indices 1..\(paramCount)" - } else { - "function \(funcDecl.name) has no parameters" - } - throw DiagnosticError("pointer index out of bounds", node: pointerArg.original, - notes: [Note(node: Syntax(funcDecl.name), message: MacroExpansionNoteMessage(noteMessage))]) - } - if argByIndex[i] != nil { - throw DiagnosticError("multiple PointerParams referring to parameter with index " + - "\(i): \(pointerArg) and \(argByIndex[i]!)", node: pointerArg.original) - } - argByIndex[i] = pointerArg - } - } - - static func setNonescapingPointers(_ args: inout [ParamInfo], _ nonescapingPointers: Set) { - for i in 0...args.count - 1 where nonescapingPointers.contains(args[i].pointerIndex) { - args[i].nonescaping = true - } - } - - public static func expansion( - of node: AttributeSyntax, - providingPeersOf declaration: some DeclSyntaxProtocol, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - do { - guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else { - throw DiagnosticError("@PointerBounds only works on functions", node: declaration) - } - - let argumentList = node.arguments!.as(LabeledExprListSyntax.self)! - var nonescapingPointers = Set() - var parsedArgs = try argumentList.compactMap { try parseMacroParam($0, funcDecl.signature, nonescapingPointers: &nonescapingPointers) } - setNonescapingPointers(&parsedArgs, nonescapingPointers) - try checkArgs(parsedArgs, funcDecl) - let baseBuilder = FunctionCallBuilder(funcDecl) - - let variant = Variant(generateSpan: hasSafeVariants(parsedArgs), skipTrivialCount: hasTrivialCountVariants(parsedArgs)) - - let builder: BoundsCheckedThunkBuilder = parsedArgs.reduce(baseBuilder, { (prev, parsedArg) in - parsedArg.getBoundsCheckedThunkBuilder(prev, funcDecl, variant) + static func parseEnumName(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> String { + guard let calledExpr = enumConstructorExpr.calledExpression.as(MemberAccessExprSyntax.self) + else { + throw DiagnosticError( + "expected PointerParam enum literal as argument, got '\(enumConstructorExpr)'", + node: enumConstructorExpr) + } + return calledExpr.declName.baseName.text + } + + static func getIntLiteralValue(_ expr: ExprSyntax) throws -> Int { + guard let intLiteral = expr.as(IntegerLiteralExprSyntax.self) else { + throw DiagnosticError("expected integer literal, got '\(expr)'", node: expr) + } + guard let res = intLiteral.representedLiteralValue else { + throw DiagnosticError("expected integer literal, got '\(expr)'", node: expr) + } + return res + } + + static func getBoolLiteralValue(_ expr: ExprSyntax) throws -> Bool { + guard let boolLiteral = expr.as(BooleanLiteralExprSyntax.self) else { + throw DiagnosticError("expected boolean literal, got '\(expr)'", node: expr) + } + switch boolLiteral.literal.tokenKind { + case .keyword(.true): + return true + case .keyword(.false): + return false + default: + throw DiagnosticError("expected bool literal, got '\(expr)'", node: expr) + } + } + + static func parseCountedByEnum( + _ enumConstructorExpr: FunctionCallExprSyntax, _ signature: FunctionSignatureSyntax + ) throws -> ParamInfo { + let argumentList = enumConstructorExpr.arguments + let pointerParamIndexArg = try getArgumentByName(argumentList, "pointer") + let pointerParamIndex: Int = try getIntLiteralValue(pointerParamIndexArg) + let countExprArg = try getArgumentByName(argumentList, "count") + guard let countExprStringLit = countExprArg.as(StringLiteralExprSyntax.self) else { + throw DiagnosticError( + "expected string literal for 'count' parameter, got \(countExprArg)", node: countExprArg) + } + let unwrappedCountExpr = ExprSyntax(stringLiteral: countExprStringLit.representedLiteralValue!) + if let countVar = unwrappedCountExpr.as(DeclReferenceExprSyntax.self) { + // Perform this lookup here so we can override the position to point to the string literal + // instead of line 1, column 1 + do { + _ = try getParameterIndexForDeclRef(signature.parameterClause.parameters, countVar) + } catch let error as DiagnosticError { + throw DiagnosticError(error.description, node: countExprStringLit, notes: error.notes) + } + } + return CountedBy( + pointerIndex: pointerParamIndex, count: unwrappedCountExpr, sizedBy: false, + nonescaping: false, original: ExprSyntax(enumConstructorExpr)) + } + + static func parseSizedByEnum(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> ParamInfo { + let argumentList = enumConstructorExpr.arguments + let pointerParamIndexArg = try getArgumentByName(argumentList, "pointer") + let pointerParamIndex: Int = try getIntLiteralValue(pointerParamIndexArg) + let sizeExprArg = try getArgumentByName(argumentList, "size") + guard let sizeExprStringLit = sizeExprArg.as(StringLiteralExprSyntax.self) else { + throw DiagnosticError( + "expected string literal for 'size' parameter, got \(sizeExprArg)", node: sizeExprArg) + } + let unwrappedCountExpr = ExprSyntax(stringLiteral: sizeExprStringLit.representedLiteralValue!) + return CountedBy( + pointerIndex: pointerParamIndex, count: unwrappedCountExpr, sizedBy: true, nonescaping: false, + original: ExprSyntax(enumConstructorExpr)) + } + + static func parseEndedByEnum(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> ParamInfo { + let argumentList = enumConstructorExpr.arguments + let startParamIndexArg = try getArgumentByName(argumentList, "start") + let startParamIndex: Int = try getIntLiteralValue(startParamIndexArg) + let endParamIndexArg = try getArgumentByName(argumentList, "end") + let endParamIndex: Int = try getIntLiteralValue(endParamIndexArg) + let nonescapingExprArg = getOptionalArgumentByName(argumentList, "nonescaping") + let nonescaping = + if nonescapingExprArg != nil { + try getBoolLiteralValue(nonescapingExprArg!) + } else { + false + } + return EndedBy( + pointerIndex: startParamIndex, endIndex: endParamIndex, nonescaping: nonescaping, + original: ExprSyntax(enumConstructorExpr)) + } + + static func parseNonEscaping(_ enumConstructorExpr: FunctionCallExprSyntax) throws -> Int { + let argumentList = enumConstructorExpr.arguments + let pointerParamIndexArg = try getArgumentByName(argumentList, "pointer") + let pointerParamIndex: Int = try getIntLiteralValue(pointerParamIndexArg) + return pointerParamIndex + } + + static func parseMacroParam( + _ paramAST: LabeledExprSyntax, _ signature: FunctionSignatureSyntax, + nonescapingPointers: inout Set + ) throws -> ParamInfo? { + let paramExpr = paramAST.expression + guard let enumConstructorExpr = paramExpr.as(FunctionCallExprSyntax.self) else { + throw DiagnosticError( + "expected PointerParam enum literal as argument, got '\(paramExpr)'", node: paramExpr) + } + let enumName = try parseEnumName(enumConstructorExpr) + switch enumName { + case "countedBy": return try parseCountedByEnum(enumConstructorExpr, signature) + case "sizedBy": return try parseSizedByEnum(enumConstructorExpr) + case "endedBy": return try parseEndedByEnum(enumConstructorExpr) + case "nonescaping": + let index = try parseNonEscaping(enumConstructorExpr) + nonescapingPointers.insert(index) + return nil + default: + throw DiagnosticError( + "expected 'countedBy', 'sizedBy', 'endedBy' or 'nonescaping', got '\(enumName)'", + node: enumConstructorExpr) + } + } + + static func hasSafeVariants(_ parsedArgs: [ParamInfo]) -> Bool { + return parsedArgs.contains { $0.nonescaping } + } + + static func hasTrivialCountVariants(_ parsedArgs: [ParamInfo]) -> Bool { + let countExprs = parsedArgs.compactMap { + switch $0 { + case let c as CountedBy: return c.count + default: return nil + } + } + let trivialCounts = countExprs.filter { + $0.is(DeclReferenceExprSyntax.self) || $0.is(IntegerLiteralExprSyntax.self) + } + // don't generate trivial count variants if there are any non-trivial counts + if trivialCounts.count < countExprs.count { + return false + } + let countVars = trivialCounts.filter { $0.is(DeclReferenceExprSyntax.self) } + let distinctCountVars = Set( + countVars.map { + return $0.as(DeclReferenceExprSyntax.self)!.baseName.text + }) + // don't generate trivial count variants if two count expressions refer to the same parameter + return countVars.count == distinctCountVars.count + } + + static func checkArgs(_ args: [ParamInfo], _ funcDecl: FunctionDeclSyntax) throws { + var argByIndex: [Int: ParamInfo] = [:] + let paramCount = funcDecl.signature.parameterClause.parameters.count + try args.forEach { pointerArg in + let i = pointerArg.pointerIndex + if i < 1 || i > paramCount { + let noteMessage = + if paramCount > 0 { + "function \(funcDecl.name) has parameter indices 1..\(paramCount)" + } else { + "function \(funcDecl.name) has no parameters" + } + throw DiagnosticError( + "pointer index out of bounds", node: pointerArg.original, + notes: [ + Note(node: Syntax(funcDecl.name), message: MacroExpansionNoteMessage(noteMessage)) + ]) + } + if argByIndex[i] != nil { + throw DiagnosticError( + "multiple PointerParams referring to parameter with index " + + "\(i): \(pointerArg) and \(argByIndex[i]!)", node: pointerArg.original) + } + argByIndex[i] = pointerArg + } + } + + static func setNonescapingPointers(_ args: inout [ParamInfo], _ nonescapingPointers: Set) { + for i in 0...args.count - 1 where nonescapingPointers.contains(args[i].pointerIndex) { + args[i].nonescaping = true + } + } + + public static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + do { + guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else { + throw DiagnosticError("@PointerBounds only works on functions", node: declaration) + } + + let argumentList = node.arguments!.as(LabeledExprListSyntax.self)! + var nonescapingPointers = Set() + var parsedArgs = try argumentList.compactMap { + try parseMacroParam($0, funcDecl.signature, nonescapingPointers: &nonescapingPointers) + } + setNonescapingPointers(&parsedArgs, nonescapingPointers) + try checkArgs(parsedArgs, funcDecl) + let baseBuilder = FunctionCallBuilder(funcDecl) + + let variant = Variant( + generateSpan: hasSafeVariants(parsedArgs), + skipTrivialCount: hasTrivialCountVariants(parsedArgs)) + + let builder: BoundsCheckedThunkBuilder = parsedArgs.reduce( + baseBuilder, + { (prev, parsedArg) in + parsedArg.getBoundsCheckedThunkBuilder(prev, funcDecl, variant) }) - let newSignature = try builder.buildFunctionSignature([:], variant) - let checks = if variant.skipTrivialCount { - [] as [CodeBlockItemSyntax] + let newSignature = try builder.buildFunctionSignature([:], variant) + let checks = + if variant.skipTrivialCount { + [] as [CodeBlockItemSyntax] } else { - try builder.buildBoundsChecks(variant).map { e in - CodeBlockItemSyntax(leadingTrivia: "\n", item: e) + try builder.buildBoundsChecks(variant).map { e in + CodeBlockItemSyntax(leadingTrivia: "\n", item: e) + } + } + let call = CodeBlockItemSyntax( + item: CodeBlockItemSyntax.Item( + ReturnStmtSyntax( + returnKeyword: .keyword(.return, trailingTrivia: " "), + expression: try builder.buildFunctionCall([:], variant)))) + let body = CodeBlockSyntax(statements: CodeBlockItemListSyntax(checks + [call])) + let newFunc = + funcDecl + .with(\.signature, newSignature) + .with(\.body, body) + .with( + \.attributes, + funcDecl.attributes.filter { e in + switch e { + case .attribute(let attr): + // don't apply this macro recursively, and avoid dupe _alwaysEmitIntoClient + let name = attr.attributeName.as(IdentifierTypeSyntax.self)?.name.text + return name == nil || (name != "PointerBounds" && name != "_alwaysEmitIntoClient") + default: return true } - } - let call = CodeBlockItemSyntax(item: CodeBlockItemSyntax.Item(ReturnStmtSyntax(returnKeyword: .keyword(.return, trailingTrivia: " "), - expression: try builder.buildFunctionCall([:], variant)))) - let body = CodeBlockSyntax(statements: CodeBlockItemListSyntax(checks + [call])) - let newFunc = funcDecl - .with(\.signature, newSignature) - .with(\.body, body) - .with(\.attributes, funcDecl.attributes.filter { e in - switch e { - case .attribute(let attr): - // don't apply this macro recursively, and avoid dupe _alwaysEmitIntoClient - let name = attr.attributeName.as(IdentifierTypeSyntax.self)?.name.text - return name == nil || (name != "PointerBounds" && name != "_alwaysEmitIntoClient") - default: return true - } - } + [.attribute(AttributeSyntax(atSign: .atSignToken(), - attributeName: IdentifierTypeSyntax(name: "_alwaysEmitIntoClient")))]) - return [DeclSyntax(newFunc)] - } catch let error as DiagnosticError { - context.diagnose(Diagnostic(node: error.node, message: MacroExpansionErrorMessage(error.description), - notes: error.notes)) - return [] - } - } + } + [ + .attribute( + AttributeSyntax( + atSign: .atSignToken(), + attributeName: IdentifierTypeSyntax(name: "_alwaysEmitIntoClient"))) + ]) + return [DeclSyntax(newFunc)] + } catch let error as DiagnosticError { + context.diagnose( + Diagnostic( + node: error.node, message: MacroExpansionErrorMessage(error.description), + notes: error.notes)) + return [] + } + } } - From 133789f5e8595a1b8c99355661be63868e0020f0 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Mon, 4 Nov 2024 21:38:35 -0800 Subject: [PATCH 07/13] add util properties for TypeSyntaxProtocol --- .../SwiftMacros/PointerBoundsMacro.swift | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift index 106c5a25a43b2..951249cdbd620 100644 --- a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift @@ -315,7 +315,7 @@ struct CountedOrSizedPointerThunkBuilder: PointerBoundsThunkBuilder { } func castIntToTargetType(expr: ExprSyntax, type: TypeSyntax) -> ExprSyntax { - if type.isSwiftInt { + if type.canRepresentBasicType(type: Int.self) { return expr } return ExprSyntax("\(type)(exactly: \(expr))!") @@ -717,3 +717,49 @@ public struct PointerBoundsMacro: PeerMacro { } } } + +// MARK: syntax utils +extension TypeSyntaxProtocol { + public var isSwiftCoreModule: Bool { + guard let identifierType = self.as(IdentifierTypeSyntax.self) else { + return false + } + return identifierType.name.text == "Swift" + } + + /// Check if this syntax could resolve to the type passed. Only supports types where the canonical type + /// can be named using only IdentifierTypeSyntax and MemberTypeSyntax. A non-exhaustive list of unsupported + /// types includes: + /// * array types + /// * function types + /// * optional types + /// * tuple types (including Void!) + /// The type syntax is allowed to use any level of qualified name for the type, e.g. Swift.Int.self + /// will match against both "Swift.Int" and "Int". + /// + /// - Parameter type: Type to check against. NB: if passing a type alias, the canonical type will be used. + /// - Returns: true if `self` spells out some suffix of the fully qualified name of `type`, otherwise false + public func canRepresentBasicType(type: Any.Type) -> Bool { + let qualifiedTypeName = String(reflecting: type) + var typeNames = qualifiedTypeName.split(separator: ".") + var currType: TypeSyntaxProtocol = self + + while !typeNames.isEmpty { + let typeName = typeNames.popLast()! + if let identifierType = currType.as(IdentifierTypeSyntax.self) { + // It doesn't matter whether this is the final element of typeNames, because we don't know + // surrounding context - the Foo.Bar.Baz type can be referred to as `Baz` inside Foo.Bar + return identifierType.name.text == typeName + } else if let memberType = currType.as(MemberTypeSyntax.self) { + if memberType.name.text != typeName { + return false + } + currType = memberType.baseType + } else { + return false + } + } + + return false + } +} \ No newline at end of file From e06c1d6a08f963f6385b07adeb7c751e13fc4ab2 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Tue, 5 Nov 2024 16:54:59 -0800 Subject: [PATCH 08/13] don't use if- or switch-expressions --- .../SwiftMacros/PointerBoundsMacro.swift | 107 +++++++++--------- 1 file changed, 51 insertions(+), 56 deletions(-) diff --git a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift index 951249cdbd620..16898f4102e53 100644 --- a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift @@ -95,7 +95,7 @@ struct DiagnosticError: Error { } } -enum UnsafePointerKind { +enum Mutability { case Immutable case Mutable } @@ -125,6 +125,33 @@ func replaceTypeName(_ type: TypeSyntax, _ name: TokenSyntax) -> TypeSyntax { return TypeSyntax(idType.with(\.name, name)) } +func getPointerMutability(text: String) -> Mutability { + switch text { + case "UnsafePointer": return .Immutable + case "UnsafeMutablePointer": return .Mutable + case "UnsafeRawPointer": return .Immutable + case "UnsafeMutableRawPointer": return .Mutable + default: + throw DiagnosticError( + "expected Unsafe[Mutable][Raw]Pointer type for type \(prev)" + + " - first type token is '\(text)'", node: name) + } +} + +func getSafePointerName(mut: Mutability, generateSpan: Bool, isRaw: Bool) -> TokenSyntax { + switch (mut, generateSpan, isRaw) { + case (.Immutable, true, true): return "RawSpan" + case (.Mutable, true, true): return "MutableRawSpan" + case (.Immutable, false, true): return "UnsafeRawBufferPointer" + case (.Mutable, false, true): return "UnsafeMutableRawBufferPointer" + + case (.Immutable, true, false): return "Span" + case (.Mutable, true, false): return "MutableSpan" + case (.Immutable, false, false): return "UnsafeBufferPointer" + case (.Mutable, false, false): return "UnsafeMutableBufferPointer" + } +} + func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) throws -> TypeSyntax { if let optType = prev.as(OptionalTypeSyntax.self) { return TypeSyntax( @@ -135,37 +162,16 @@ func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) th } let name = try getTypeName(prev) let text = name.text - let kind: UnsafePointerKind = - switch text { - case "UnsafePointer": .Immutable - case "UnsafeMutablePointer": .Mutable - case "UnsafeRawPointer": .Immutable - case "UnsafeMutableRawPointer": .Mutable - default: - throw DiagnosticError( - "expected Unsafe[Mutable][Raw]Pointer type for type \(prev)" - + " - first type token is '\(text)'", node: name) - } + if !isSizedBy && (text == "UnsafeRawPointer" || text == "UnsafeMutableRawPointer") { + throw DiagnosticError("raw pointers only supported for SizedBy", node: name) + } + + let kind: Mutability = + getPointerMutability(text: text) + let token = getSafePointerName(mut: kind, generateSpan: variant.generateSpan, isRaw: isSizedBy) if isSizedBy { - let token: TokenSyntax = - switch (kind, variant.generateSpan) { - case (.Immutable, true): "RawSpan" - case (.Mutable, true): "MutableRawSpan" - case (.Immutable, false): "UnsafeRawBufferPointer" - case (.Mutable, false): "UnsafeMutableRawBufferPointer" - } return TypeSyntax(IdentifierTypeSyntax(name: token)) } - if text == "UnsafeRawPointer" || text == "UnsafeMutableRawPointer" { - throw DiagnosticError("raw pointers only supported for SizedBy", node: name) - } - let token: TokenSyntax = - switch (kind, variant.generateSpan) { - case (.Immutable, true): "Span" - case (.Mutable, true): "MutableSpan" - case (.Immutable, false): "UnsafeBufferPointer" - case (.Mutable, false): "UnsafeMutableBufferPointer" - } return replaceTypeName(prev, token) } @@ -183,13 +189,11 @@ protocol BoundsCheckedThunkBuilder { func getParam(_ signature: FunctionSignatureSyntax, _ paramIndex: Int) -> FunctionParameterSyntax { let params = signature.parameterClause.parameters - let index = - if paramIndex > 0 { - params.index(params.startIndex, offsetBy: paramIndex) - } else { - params.startIndex - } - return params[index] + if paramIndex > 0 { + return params[params.index(params.startIndex, offsetBy: paramIndex)] + } else { + return params[params.startIndex] + } } func getParam(_ funcDecl: FunctionDeclSyntax, _ paramIndex: Int) -> FunctionParameterSyntax { return getParam(funcDecl.signature, paramIndex) @@ -342,19 +346,17 @@ struct CountedOrSizedPointerThunkBuilder: PointerBoundsThunkBuilder { func getCount(_ variant: Variant) -> ExprSyntax { let countName = isSizedBy && variant.generateSpan ? "byteCount" : "count" - return if nullable { - ExprSyntax("\(name)?.\(raw: countName) ?? 0") - } else { - ExprSyntax("\(name).\(raw: countName)") + if nullable { + return ExprSyntax("\(name)?.\(raw: countName) ?? 0") } + return ExprSyntax("\(name).\(raw: countName)") } func getPointerArg() -> ExprSyntax { - return if nullable { - ExprSyntax("\(name)?.baseAddress") - } else { - ExprSyntax("\(name).baseAddress!") + if nullable { + return ExprSyntax("\(name)?.baseAddress") } + return ExprSyntax("\(name).baseAddress!") } func buildFunctionCall(_ argOverrides: [Int: ExprSyntax], _ variant: Variant) throws -> ExprSyntax @@ -541,12 +543,7 @@ public struct PointerBoundsMacro: PeerMacro { let endParamIndexArg = try getArgumentByName(argumentList, "end") let endParamIndex: Int = try getIntLiteralValue(endParamIndexArg) let nonescapingExprArg = getOptionalArgumentByName(argumentList, "nonescaping") - let nonescaping = - if nonescapingExprArg != nil { - try getBoolLiteralValue(nonescapingExprArg!) - } else { - false - } + let nonescaping = nonescapingExprArg != nil && try getBoolLiteralValue(nonescapingExprArg!) return EndedBy( pointerIndex: startParamIndex, endIndex: endParamIndex, nonescaping: nonescaping, original: ExprSyntax(enumConstructorExpr)) @@ -618,11 +615,10 @@ public struct PointerBoundsMacro: PeerMacro { let i = pointerArg.pointerIndex if i < 1 || i > paramCount { let noteMessage = - if paramCount > 0 { + paramCount > 0 ? "function \(funcDecl.name) has parameter indices 1..\(paramCount)" - } else { + : "function \(funcDecl.name) has no parameters" - } throw DiagnosticError( "pointer index out of bounds", node: pointerArg.original, notes: [ @@ -674,13 +670,12 @@ public struct PointerBoundsMacro: PeerMacro { }) let newSignature = try builder.buildFunctionSignature([:], variant) let checks = - if variant.skipTrivialCount { + variant.skipTrivialCount ? [] as [CodeBlockItemSyntax] - } else { + : try builder.buildBoundsChecks(variant).map { e in CodeBlockItemSyntax(leadingTrivia: "\n", item: e) } - } let call = CodeBlockItemSyntax( item: CodeBlockItemSyntax.Item( ReturnStmtSyntax( From 7932c900bd71af3762454cfe311b88dd3a84596a Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Wed, 6 Nov 2024 11:55:02 -0800 Subject: [PATCH 09/13] address build failures --- .../SwiftMacros/PointerBoundsMacro.swift | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift index 16898f4102e53..a445a19b9b320 100644 --- a/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/PointerBoundsMacro.swift @@ -125,16 +125,14 @@ func replaceTypeName(_ type: TypeSyntax, _ name: TokenSyntax) -> TypeSyntax { return TypeSyntax(idType.with(\.name, name)) } -func getPointerMutability(text: String) -> Mutability { +func getPointerMutability(text: String) -> Mutability? { switch text { case "UnsafePointer": return .Immutable case "UnsafeMutablePointer": return .Mutable case "UnsafeRawPointer": return .Immutable case "UnsafeMutableRawPointer": return .Mutable default: - throw DiagnosticError( - "expected Unsafe[Mutable][Raw]Pointer type for type \(prev)" - + " - first type token is '\(text)'", node: name) + return nil } } @@ -166,8 +164,11 @@ func transformType(_ prev: TypeSyntax, _ variant: Variant, _ isSizedBy: Bool) th throw DiagnosticError("raw pointers only supported for SizedBy", node: name) } - let kind: Mutability = - getPointerMutability(text: text) + guard let kind: Mutability = getPointerMutability(text: text) else { + throw DiagnosticError( + "expected Unsafe[Mutable][Raw]Pointer type for type \(prev)" + + " - first type token is '\(text)'", node: name) + } let token = getSafePointerName(mut: kind, generateSpan: variant.generateSpan, isRaw: isSizedBy) if isSizedBy { return TypeSyntax(IdentifierTypeSyntax(name: token)) @@ -543,7 +544,7 @@ public struct PointerBoundsMacro: PeerMacro { let endParamIndexArg = try getArgumentByName(argumentList, "end") let endParamIndex: Int = try getIntLiteralValue(endParamIndexArg) let nonescapingExprArg = getOptionalArgumentByName(argumentList, "nonescaping") - let nonescaping = nonescapingExprArg != nil && try getBoolLiteralValue(nonescapingExprArg!) + let nonescaping = try nonescapingExprArg != nil && getBoolLiteralValue(nonescapingExprArg!) return EndedBy( pointerIndex: startParamIndex, endIndex: endParamIndex, nonescaping: nonescaping, original: ExprSyntax(enumConstructorExpr)) @@ -615,10 +616,9 @@ public struct PointerBoundsMacro: PeerMacro { let i = pointerArg.pointerIndex if i < 1 || i > paramCount { let noteMessage = - paramCount > 0 ? - "function \(funcDecl.name) has parameter indices 1..\(paramCount)" - : - "function \(funcDecl.name) has no parameters" + paramCount > 0 + ? "function \(funcDecl.name) has parameter indices 1..\(paramCount)" + : "function \(funcDecl.name) has no parameters" throw DiagnosticError( "pointer index out of bounds", node: pointerArg.original, notes: [ @@ -670,12 +670,11 @@ public struct PointerBoundsMacro: PeerMacro { }) let newSignature = try builder.buildFunctionSignature([:], variant) let checks = - variant.skipTrivialCount ? - [] as [CodeBlockItemSyntax] - : - try builder.buildBoundsChecks(variant).map { e in - CodeBlockItemSyntax(leadingTrivia: "\n", item: e) - } + variant.skipTrivialCount + ? [] as [CodeBlockItemSyntax] + : try builder.buildBoundsChecks(variant).map { e in + CodeBlockItemSyntax(leadingTrivia: "\n", item: e) + } let call = CodeBlockItemSyntax( item: CodeBlockItemSyntax.Item( ReturnStmtSyntax( @@ -757,4 +756,4 @@ extension TypeSyntaxProtocol { return false } -} \ No newline at end of file +} From fed90490b42cc193c88f4064041752e17b9d5115 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Wed, 6 Nov 2024 17:02:52 -0800 Subject: [PATCH 10/13] fix builds support tests --- utils/build_swift/tests/expected_options.py | 1 + .../tests/products/test_swift.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index ea0add221a89e..f2eaa18ca227a 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -185,6 +185,7 @@ 'enable_experimental_string_processing': True, 'enable_experimental_observation': True, 'enable_experimental_parser_validation': True, + 'enable_experimental_pointer_bounds': False, 'swift_enable_backtracing': True, 'enable_synchronization': True, 'enable_volatile': True, diff --git a/utils/swift_build_support/tests/products/test_swift.py b/utils/swift_build_support/tests/products/test_swift.py index 6a395197412c4..da32303acb70f 100644 --- a/utils/swift_build_support/tests/products/test_swift.py +++ b/utils/swift_build_support/tests/products/test_swift.py @@ -61,6 +61,7 @@ def setUp(self): enable_experimental_nonescapable_types=False, enable_experimental_observation=False, enable_experimental_parser_validation=False, + enable_experimental_pointer_bounds=False, swift_enable_backtracing=False, enable_synchronization=False, enable_volatile=False, @@ -110,6 +111,7 @@ def test_by_default_no_cmake_options(self): '-DSWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED:BOOL=FALSE', '-DSWIFT_ENABLE_EXPERIMENTAL_OBSERVATION:BOOL=FALSE', '-DSWIFT_ENABLE_EXPERIMENTAL_PARSER_VALIDATION:BOOL=FALSE', + '-DSWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS:BOOL=FALSE', '-DSWIFT_ENABLE_BACKTRACING:BOOL=FALSE', '-DSWIFT_ENABLE_SYNCHRONIZATION:BOOL=FALSE', '-DSWIFT_ENABLE_VOLATILE:BOOL=FALSE', @@ -144,6 +146,7 @@ def test_swift_runtime_tsan(self): '-DSWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED:BOOL=FALSE', '-DSWIFT_ENABLE_EXPERIMENTAL_OBSERVATION:BOOL=FALSE', '-DSWIFT_ENABLE_EXPERIMENTAL_PARSER_VALIDATION:BOOL=FALSE', + '-DSWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS:BOOL=FALSE', '-DSWIFT_ENABLE_BACKTRACING:BOOL=FALSE', '-DSWIFT_ENABLE_SYNCHRONIZATION:BOOL=FALSE', '-DSWIFT_ENABLE_VOLATILE:BOOL=FALSE', @@ -432,6 +435,19 @@ def test_experimental_observation_flags(self): [x for x in swift.cmake_options if 'DSWIFT_ENABLE_EXPERIMENTAL_OBSERVATION' in x]) + def test_experimental_pointer_bounds_flags(self): + self.args.enable_experimental_pointer_bounds = True + swift = Swift( + args=self.args, + toolchain=self.toolchain, + source_dir='/path/to/src', + build_dir='/path/to/build') + self.assertEqual( + ['-DSWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS:BOOL=' + 'TRUE'], + [x for x in swift.cmake_options + if 'DSWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS' in x]) + def test_backtracing_flags(self): self.args.swift_enable_backtracing = True swift = Swift( From b2546231d736ccbe606d9764c0288628778eb737 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 7 Nov 2024 23:18:26 -0800 Subject: [PATCH 11/13] Fix Python linting issue --- .../swift_build_support/products/wasmstdlib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/swift_build_support/swift_build_support/products/wasmstdlib.py b/utils/swift_build_support/swift_build_support/products/wasmstdlib.py index fdd64e2b21319..a0558ffa82703 100644 --- a/utils/swift_build_support/swift_build_support/products/wasmstdlib.py +++ b/utils/swift_build_support/swift_build_support/products/wasmstdlib.py @@ -164,7 +164,8 @@ def _build_stdlib(self, host_target, target_triple, llvm_cmake_dir): self.cmake_options.define('SWIFT_ENABLE_SYNCHRONIZATION:BOOL', 'TRUE') self.cmake_options.define('SWIFT_ENABLE_VOLATILE:BOOL', 'TRUE') self.cmake_options.define('SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION:BOOL', 'TRUE') - self.cmake_options.define('SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS:BOOL', 'TRUE') + self.cmake_options.define('SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS:BOOL', + 'TRUE') self.add_extra_cmake_options() From eb96e514791d757f3804c95c3ac790af1031f34e Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Fri, 8 Nov 2024 12:29:28 -0800 Subject: [PATCH 12/13] remove Span stubs --- stdlib/public/core/PointerBounds.swift | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/stdlib/public/core/PointerBounds.swift b/stdlib/public/core/PointerBounds.swift index 9cee32a66e210..6528a079f55b6 100644 --- a/stdlib/public/core/PointerBounds.swift +++ b/stdlib/public/core/PointerBounds.swift @@ -42,27 +42,3 @@ public enum PointerParam { @attached(peer, names: overloaded) public macro PointerBounds(_ paramInfo: PointerParam...) = #externalMacro(module: "SwiftMacros", type: "PointerBoundsMacro") - -// Stub interfaces for testing until Span lands -public struct Span { - public var count: Int - var ptr: UnsafeBufferPointer - public func withUnsafeBufferPointer(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R { - return try body(ptr) - } -} -public protocol RawSpan { - var byteCount: Int { get } - func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R -} -public struct MutableSpan { - public var count: Int - var ptr: UnsafeMutableBufferPointer - public func withUnsafeBufferPointer(_ body: (UnsafeMutableBufferPointer) throws -> R) rethrows -> R { - return try body(ptr) - } -} -public protocol MutableRawSpan { - var byteCount: Int { get } - func withUnsafeBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R -} From ae716bcb339beb91c10f7cafb3f1bba99a9ccd17 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 11 Nov 2024 09:49:32 -0800 Subject: [PATCH 13/13] XFAIL tests that can't work without more library support --- test/Macros/PointerBounds/CountedBy/MutableSpan.swift | 2 +- test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Macros/PointerBounds/CountedBy/MutableSpan.swift b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift index 37c637774a8ff..e874fbf3aa463 100644 --- a/test/Macros/PointerBounds/CountedBy/MutableSpan.swift +++ b/test/Macros/PointerBounds/CountedBy/MutableSpan.swift @@ -1,6 +1,6 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds - +// XFAIL: OS=windows-msvc // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.countedBy(pointer: 1, count: "len"), .nonescaping(pointer: 1)) diff --git a/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift index 0be291e5ab985..1d216e02d62ba 100644 --- a/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift +++ b/test/Macros/PointerBounds/SizedBy/MutableRawSpan.swift @@ -1,6 +1,6 @@ // REQUIRES: swift_swift_parser // REQUIRES: pointer_bounds - +// XFAIL: OS=windows-msvc // RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s @PointerBounds(.sizedBy(pointer: 1, size: "size"), .nonescaping(pointer: 1))