mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[ConstExpr] Relax expectations in ArrayAppendElement to allow direct-passed element
This commit is contained in:
@@ -1011,8 +1011,12 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply,
|
||||
conventions.getNumDirectSILResults() == 0 &&
|
||||
conventions.getNumIndirectSILResults() == 0 &&
|
||||
"unexpected Array.append(_:) signature");
|
||||
// Get the element to be appended which is passed indirectly (@in).
|
||||
SymbolicValue element = getConstAddrAndLoadResult(apply->getOperand(1));
|
||||
// Get the element to be appended which is usually passed indirectly (@in),
|
||||
// or directly (in Embedded Swift where Array.append can be specialized).
|
||||
SymbolicValue element = getConstantValue(apply->getOperand(1));
|
||||
if (element.getKind() == SymbolicValue::Address) {
|
||||
element = getConstAddrAndLoadResult(apply->getOperand(1));
|
||||
}
|
||||
if (!element.isConstant())
|
||||
return element;
|
||||
|
||||
|
||||
732
test/embedded/Inputs/MyCustomMessage2.swift
Normal file
732
test/embedded/Inputs/MyCustomMessage2.swift
Normal file
@@ -0,0 +1,732 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2021 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
extension String {
|
||||
/// Replace all percents "%" in the string by "%%" so that the string can be
|
||||
/// interpreted as a C format string. This function is constant evaluable
|
||||
/// and its semantics is modeled within the evaluator.
|
||||
@inlinable
|
||||
internal var percentEscapedString: String {
|
||||
@_semantics("string.escapePercent.get")
|
||||
@_effects(readonly)
|
||||
@_optimize(none)
|
||||
get {
|
||||
return self
|
||||
.split(separator: "%", omittingEmptySubsequences: false)
|
||||
.joined(separator: "%%")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MyCustomInterpolation {
|
||||
|
||||
/// Defines interpolation for UnsafeRawBufferPointer.
|
||||
///
|
||||
/// Do not call this function directly.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - pointer: The interpolated expression of type `UnsafeRawBufferPointer`,
|
||||
/// which is autoclosured.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ pointer: @autoclosure @escaping () -> UnsafeRawBufferPointer
|
||||
) {
|
||||
appendInterpolation(pointer().baseAddress!)
|
||||
}
|
||||
|
||||
/// Defines interpolation for UnsafeRawPointer.
|
||||
///
|
||||
/// Do not call this function directly.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - pointer: The interpolated expression of type `UnsafeRawPointer`, which is autoclosured.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ pointer: @autoclosure @escaping () -> UnsafeRawPointer
|
||||
) {
|
||||
formatString += "%p"
|
||||
}
|
||||
}
|
||||
|
||||
@frozen
|
||||
public struct MyCustomIntegerFormatting {
|
||||
/// The base to use for the string representation. `radix` must be at least 2
|
||||
/// and at most 36. The default is 10.
|
||||
@usableFromInline
|
||||
internal var radix: Int
|
||||
|
||||
/// When set, a `+` will be printed for all non-negative integers.
|
||||
@usableFromInline
|
||||
internal var explicitPositiveSign: Bool
|
||||
|
||||
/// When set, a prefix: 0b or 0o or 0x will be added when the radix is 2, 8 or
|
||||
/// 16 respectively.
|
||||
@usableFromInline
|
||||
internal var includePrefix: Bool
|
||||
|
||||
/// Whether to use uppercase letters to represent numerals
|
||||
/// greater than 9 (default is to use lowercase).
|
||||
@usableFromInline
|
||||
internal var uppercase: Bool
|
||||
|
||||
/// Minimum number of digits to display. Numbers having fewer digits than
|
||||
/// minDigits will be displayed with leading zeros.
|
||||
@usableFromInline
|
||||
internal var minDigits: (() -> Int)?
|
||||
|
||||
/// Initializes all stored properties.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - radix: The base to use for the string representation. `radix` must be
|
||||
/// at least 2 and at most 36. The default is 10.
|
||||
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
||||
/// numbers. Default is `false`.
|
||||
/// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding
|
||||
/// radices. Default is `false`.
|
||||
/// - uppercase: Pass `true` to use uppercase letters to represent numerals
|
||||
/// greater than 9, or `false` to use lowercase letters. The default is
|
||||
/// `false`.
|
||||
/// - minDigits: minimum number of digits to display. Numbers will be
|
||||
/// prefixed with zeros if necessary to meet the minimum. The default is 1.
|
||||
@_transparent
|
||||
@usableFromInline
|
||||
internal init(
|
||||
radix: Int = 10,
|
||||
explicitPositiveSign: Bool = false,
|
||||
includePrefix: Bool = false,
|
||||
uppercase: Bool = false,
|
||||
minDigits: (() -> Int)?
|
||||
) {
|
||||
self.radix = radix
|
||||
self.explicitPositiveSign = explicitPositiveSign
|
||||
self.includePrefix = includePrefix
|
||||
self.uppercase = uppercase
|
||||
self.minDigits = minDigits
|
||||
}
|
||||
|
||||
/// Displays an interpolated integer as a decimal number with the specified number
|
||||
/// of digits and an optional sign.
|
||||
///
|
||||
/// The parameter `explicitPositiveSign` must be a boolean literal. The
|
||||
/// parameter `minDigits` can be an arbitrary expression.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
||||
/// numbers.
|
||||
/// - minDigits: minimum number of digits to display. Numbers will be
|
||||
/// prefixed with zeros if necessary to meet the minimum.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public static func decimal(
|
||||
explicitPositiveSign: Bool = false,
|
||||
minDigits: @escaping @autoclosure () -> Int
|
||||
) -> MyCustomIntegerFormatting {
|
||||
return MyCustomIntegerFormatting(
|
||||
radix: 10,
|
||||
explicitPositiveSign: explicitPositiveSign,
|
||||
minDigits: minDigits)
|
||||
}
|
||||
|
||||
/// Displays an interpolated integer as a decimal number with an optional sign.
|
||||
///
|
||||
/// The parameter `explicitPositiveSign` must be a boolean literal.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
||||
/// numbers.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public static func decimal(
|
||||
explicitPositiveSign: Bool = false
|
||||
) -> MyCustomIntegerFormatting {
|
||||
return MyCustomIntegerFormatting(
|
||||
radix: 10,
|
||||
explicitPositiveSign: explicitPositiveSign,
|
||||
minDigits: nil)
|
||||
}
|
||||
|
||||
/// Displays an interpolated integer as a decimal number. This is the default format for
|
||||
/// integers.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public static var decimal: MyCustomIntegerFormatting { .decimal() }
|
||||
|
||||
/// Displays an interpolated unsigned integer as a hexadecimal number with the
|
||||
/// specified parameters. This formatting option should be used only with unsigned
|
||||
/// integers.
|
||||
///
|
||||
/// All parameters except `minDigits` should be boolean literals. `minDigits`
|
||||
/// can be an arbitrary expression.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
||||
/// numbers.
|
||||
/// - includePrefix: Pass `true` to add a prefix 0x.
|
||||
/// - uppercase: Pass `true` to use uppercase letters to represent numerals
|
||||
/// greater than 9, or `false` to use lowercase letters. The default is `false`.
|
||||
/// - minDigits: minimum number of digits to display. Numbers will be
|
||||
/// prefixed with zeros if necessary to meet the minimum.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public static func hex(
|
||||
explicitPositiveSign: Bool = false,
|
||||
includePrefix: Bool = false,
|
||||
uppercase: Bool = false,
|
||||
minDigits: @escaping @autoclosure () -> Int
|
||||
) -> MyCustomIntegerFormatting {
|
||||
return MyCustomIntegerFormatting(
|
||||
radix: 16,
|
||||
explicitPositiveSign: explicitPositiveSign,
|
||||
includePrefix: includePrefix,
|
||||
uppercase: uppercase,
|
||||
minDigits: minDigits)
|
||||
}
|
||||
|
||||
/// Displays an interpolated unsigned integer as a hexadecimal number with the specified
|
||||
/// parameters. This formatting option should be used only with unsigned integers.
|
||||
///
|
||||
/// All parameters should be boolean literals.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
||||
/// numbers.
|
||||
/// - includePrefix: Pass `true` to add a prefix 0x.
|
||||
/// - uppercase: Pass `true` to use uppercase letters to represent numerals
|
||||
/// greater than 9, or `false` to use lowercase letters. The default is `false`.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public static func hex(
|
||||
explicitPositiveSign: Bool = false,
|
||||
includePrefix: Bool = false,
|
||||
uppercase: Bool = false
|
||||
) -> MyCustomIntegerFormatting {
|
||||
return MyCustomIntegerFormatting(
|
||||
radix: 16,
|
||||
explicitPositiveSign: explicitPositiveSign,
|
||||
includePrefix: includePrefix,
|
||||
uppercase: uppercase,
|
||||
minDigits: nil)
|
||||
}
|
||||
|
||||
/// Displays an interpolated unsigned integer as a hexadecimal number.
|
||||
/// This formatting option should be used only with unsigned integers.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public static var hex: MyCustomIntegerFormatting { .hex() }
|
||||
|
||||
/// Displays an interpolated unsigned integer as an octal number with the specified
|
||||
/// parameters. This formatting option should be used only with unsigned
|
||||
/// integers.
|
||||
///
|
||||
/// All parameters except `minDigits` should be boolean literals. `minDigits`
|
||||
/// can be an arbitrary expression.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
||||
/// numbers.
|
||||
/// - includePrefix: Pass `true` to add a prefix 0o.
|
||||
/// - uppercase: Pass `true` to use uppercase letters to represent numerals
|
||||
/// greater than 9, or `false` to use lowercase letters. The default is `false`.
|
||||
/// - minDigits: minimum number of digits to display. Numbers will be
|
||||
/// prefixed with zeros if necessary to meet the minimum.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public static func octal(
|
||||
explicitPositiveSign: Bool = false,
|
||||
includePrefix: Bool = false,
|
||||
uppercase: Bool = false,
|
||||
minDigits: @autoclosure @escaping () -> Int
|
||||
) -> MyCustomIntegerFormatting {
|
||||
MyCustomIntegerFormatting(
|
||||
radix: 8,
|
||||
explicitPositiveSign: explicitPositiveSign,
|
||||
includePrefix: includePrefix,
|
||||
uppercase: uppercase,
|
||||
minDigits: minDigits)
|
||||
}
|
||||
|
||||
/// Displays an interpolated unsigned integer as an octal number with the specified parameters.
|
||||
/// This formatting option should be used only with unsigned integers.
|
||||
///
|
||||
/// All parameters must be boolean literals.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - explicitPositiveSign: Pass `true` to add a + sign to non-negative
|
||||
/// numbers.
|
||||
/// - includePrefix: Pass `true` to add a prefix 0o.
|
||||
/// - uppercase: Pass `true` to use uppercase letters to represent numerals
|
||||
/// greater than 9, or `false` to use lowercase letters.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public static func octal(
|
||||
explicitPositiveSign: Bool = false,
|
||||
includePrefix: Bool = false,
|
||||
uppercase: Bool = false
|
||||
) -> MyCustomIntegerFormatting {
|
||||
MyCustomIntegerFormatting(
|
||||
radix: 8,
|
||||
explicitPositiveSign: explicitPositiveSign,
|
||||
includePrefix: includePrefix,
|
||||
uppercase: uppercase,
|
||||
minDigits: nil)
|
||||
}
|
||||
|
||||
/// Displays an interpolated unsigned integer as an octal number.
|
||||
/// This formatting option should be used only with unsigned integers.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public static var octal: MyCustomIntegerFormatting { .octal() }
|
||||
}
|
||||
|
||||
extension MyCustomIntegerFormatting {
|
||||
/// The prefix for the radix in the Swift literal syntax.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
internal var _prefix: String {
|
||||
guard includePrefix else { return "" }
|
||||
switch radix {
|
||||
case 2: return "0b"
|
||||
case 8: return "0o"
|
||||
case 16: return "0x"
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension MyCustomIntegerFormatting {
|
||||
|
||||
/// Returns a fprintf-compatible length modifier for a given argument type.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
internal static func formatSpecifierLengthModifier<I: BinaryInteger>(_ type: I.Type) -> String? {
|
||||
switch MemoryLayout<I>.size {
|
||||
case 1: return "hh"
|
||||
case 2: return "h"
|
||||
case 4: return ""
|
||||
case 8: return "ll"
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs an os_log format specifier for the given `type`.
|
||||
@_semantics("constant_evaluable")
|
||||
@_alwaysEmitIntoClient
|
||||
@_optimize(none)
|
||||
@_effects(readonly)
|
||||
internal func formatSpecifier<I: BinaryInteger>(
|
||||
for type: I.Type,
|
||||
attributes: String
|
||||
) -> String {
|
||||
// Based on IEEE Std 1003.1-2017
|
||||
// `d`/`i` is the only signed integral conversions allowed
|
||||
if (type.isSigned && radix != 10) {
|
||||
fatalError("Signed integers must be formatted using .decimal")
|
||||
}
|
||||
|
||||
// IEEE: Each conversion specification is introduced by the '%' character
|
||||
// after which the following appear in sequence:
|
||||
// 1. Zero or more flags (in any order), which modify the meaning of the
|
||||
// conversion specification.
|
||||
// 2. An optional minimum field width (for alignment). If the converted
|
||||
// value has fewer bytes than the field width, it shall be padded with
|
||||
// <space> characters by default on the left; it shall be padded on the
|
||||
// right if the left-adjustment flag ( '-' ), is given to the
|
||||
// field width.
|
||||
// 3. An optional precision that gives the minimum number of digits to
|
||||
// display for the d, i, o, u, x, and X conversion specifiers.
|
||||
// 4. An optional length modifier that specifies the size of the argument.
|
||||
// 5. A conversion specifier character that indicates the type of
|
||||
// conversion to be applied.
|
||||
|
||||
// Use Swift style prefixes rather than fprintf style prefixes.
|
||||
var specification = _prefix
|
||||
specification += "%"
|
||||
|
||||
//
|
||||
// 1. Flags
|
||||
//
|
||||
// Use `+` flag if signed, otherwise prefix a literal `+` for unsigned.
|
||||
if explicitPositiveSign {
|
||||
// IEEE: `+` The result of a signed conversion shall always begin with a
|
||||
// sign ( '+' or '-' )
|
||||
if type.isSigned {
|
||||
specification += "+"
|
||||
} else {
|
||||
var newSpecification = "+"
|
||||
newSpecification += specification
|
||||
specification = newSpecification
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Precision
|
||||
|
||||
// Default precision for integers is 1, otherwise use requested precision.
|
||||
// The precision could be a dynamic value.
|
||||
// Therefore, use a star here and pass it as an additional argument.
|
||||
if let _ = minDigits {
|
||||
specification += ".*"
|
||||
}
|
||||
|
||||
// 4. Length modifier
|
||||
guard let lengthModifier =
|
||||
MyCustomIntegerFormatting.formatSpecifierLengthModifier(type) else {
|
||||
fatalError("Integer type has unknown byte length")
|
||||
}
|
||||
specification += lengthModifier
|
||||
|
||||
// 5. The conversion specifier
|
||||
switch radix {
|
||||
case 10:
|
||||
specification += type.isSigned ? "d" : "u"
|
||||
case 8:
|
||||
specification += "o"
|
||||
case 16:
|
||||
specification += uppercase ? "X" : "x"
|
||||
default:
|
||||
fatalError("radix must be 10, 8 or 16")
|
||||
}
|
||||
return specification
|
||||
}
|
||||
}
|
||||
|
||||
@frozen
|
||||
@usableFromInline
|
||||
internal struct MyCustomArguments {
|
||||
@usableFromInline
|
||||
internal var argumentClosures: [()->String]
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
internal init() {
|
||||
argumentClosures = []
|
||||
}
|
||||
|
||||
/// `append` for other types must be implemented by extensions.
|
||||
}
|
||||
|
||||
extension MyCustomInterpolation {
|
||||
|
||||
/// Defines interpolation for expressions of type Int.
|
||||
///
|
||||
/// Do not call this function directly. It will be called automatically when interpolating
|
||||
/// a value of type `Int` in the string interpolations passed to the log APIs.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - number: The interpolated expression of type Int, which is autoclosured.
|
||||
/// - format: A formatting option available for integer types, defined by the
|
||||
/// type: `MyCustomIntegerFormatting`. The default is `.decimal`.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> Int,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
// Define appendInterpolation overloads for fixed-size integers.
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> Int8,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> Int16,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> Int32,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> Int64,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
/// Defines interpolation for expressions of type UInt.
|
||||
///
|
||||
/// Do not call this function directly. It will be called automatically when interpolating
|
||||
/// a value of type `Int` in the string interpolations passed to the log APIs.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - number: The interpolated expression of type UInt, which is autoclosured.
|
||||
/// - format: A formatting option available for integer types, defined by the
|
||||
/// type `MyCustomIntegerFormatting`.
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> UInt,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
// Define appendInterpolation overloads for unsigned integers.
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> UInt8,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> UInt16,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> UInt32,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ number: @autoclosure @escaping () -> UInt64,
|
||||
format: MyCustomIntegerFormatting = .decimal
|
||||
) {
|
||||
appendInteger(number, format: format)
|
||||
}
|
||||
|
||||
/// Defines interpolation for expressions of type Int.
|
||||
///
|
||||
/// Do not call this function directly. It will be called automatically when interpolating
|
||||
/// a value of type `Int` in the string interpolations passed to the log APIs.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - number: The interpolated expression of type Int, which is autoclosured.
|
||||
/// - format: A formatting option available for integer types, defined by the
|
||||
/// type: `MyCustomIntegerFormatting`. The default is `.decimal`.
|
||||
/// - attributes: A string that specifies an attribute for the interpolated value,
|
||||
/// which can be used to provide additional information about the interpolated
|
||||
/// value to tools such as Xcode that can process and render os_log and os_signpost
|
||||
/// messages. An example of an attribute is "xcode:size-in-bytes". If the target tool
|
||||
/// that processes these messages doesn't understand the attribute it would be ignored.
|
||||
@_semantics("constant_evaluable")
|
||||
@_alwaysEmitIntoClient
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation<T: BinaryInteger>(
|
||||
_ number: @autoclosure @escaping () -> T,
|
||||
format: MyCustomIntegerFormatting = .decimal,
|
||||
attributes: String
|
||||
) {
|
||||
appendInteger(number, format: format, attributes: attributes)
|
||||
}
|
||||
|
||||
/// Given an integer, create and append a format specifier for the integer to the
|
||||
/// format string property. Also, append the integer along with necessary headers
|
||||
/// to the MyCustomArguments property.
|
||||
@_semantics("constant_evaluable")
|
||||
@_alwaysEmitIntoClient
|
||||
@_optimize(none)
|
||||
internal mutating func appendInteger<T>(
|
||||
_ number: @escaping () -> T,
|
||||
format: MyCustomIntegerFormatting,
|
||||
attributes: String = ""
|
||||
) where T: BinaryInteger {
|
||||
formatString += format.formatSpecifier(for: T.self, attributes: attributes)
|
||||
arguments.argumentClosures.append({ number().description })
|
||||
}
|
||||
}
|
||||
|
||||
@frozen
|
||||
public struct MyCustomInterpolation : StringInterpolationProtocol {
|
||||
/// A format string constructed from the given string interpolation to be
|
||||
/// passed to vprintf
|
||||
@usableFromInline
|
||||
internal var formatString: String
|
||||
|
||||
@usableFromInline
|
||||
internal var arguments: MyCustomArguments
|
||||
|
||||
// Some methods defined below are marked @_optimize(none) to prevent inlining
|
||||
// of string internals (such as String._StringGuts) which will interfere with
|
||||
// constant evaluation and folding. Note that these methods will be inlined,
|
||||
// constant evaluated/folded and optimized in the context of a caller.
|
||||
|
||||
@_semantics("oslog.interpolation.init")
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public init(literalCapacity: Int, interpolationCount: Int) {
|
||||
// Since the format string and the arguments array are fully constructed
|
||||
// at compile time, the parameters are ignored.
|
||||
formatString = ""
|
||||
arguments = MyCustomArguments()
|
||||
}
|
||||
|
||||
@_semantics("constant_evaluable")
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
public mutating func appendLiteral(_ literal: String) {
|
||||
formatString += literal.percentEscapedString
|
||||
}
|
||||
|
||||
/// `appendInterpolation` conformances will be added by extensions to this type.
|
||||
}
|
||||
|
||||
extension MyCustomInterpolation {
|
||||
/// Defines interpolation for expressions of type String.
|
||||
///
|
||||
/// Do not call this function directly. It will be called automatically when interpolating
|
||||
/// a value of type `String` in the string interpolations passed to the log APIs.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - argumentString: The interpolated expression of type String, which is autoclosured.
|
||||
@_semantics("constant_evaluable")
|
||||
@_alwaysEmitIntoClient
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation(
|
||||
_ argumentString: @autoclosure @escaping () -> String
|
||||
) {
|
||||
formatString += "%s"
|
||||
arguments.argumentClosures.append(argumentString)
|
||||
}
|
||||
}
|
||||
|
||||
extension MyCustomInterpolation {
|
||||
|
||||
/// Defines interpolation for values conforming to CustomStringConvertible. The values
|
||||
/// are displayed using the description methods on them.
|
||||
///
|
||||
/// Do not call this function directly. It will be called automatically when interpolating
|
||||
/// a value conforming to CustomStringConvertible in the string interpolations passed
|
||||
/// to the log APIs.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - value: The interpolated expression conforming to CustomStringConvertible.
|
||||
@_optimize(none)
|
||||
@_transparent
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
public mutating func appendInterpolation<T : CustomStringConvertible>(
|
||||
_ value: @autoclosure @escaping () -> T
|
||||
) {
|
||||
appendInterpolation(value().description)
|
||||
}
|
||||
}
|
||||
|
||||
@frozen @_semantics("oslog.message.type")
|
||||
public struct MyCustomMessage :
|
||||
ExpressibleByStringInterpolation, ExpressibleByStringLiteral
|
||||
{
|
||||
public let interpolation: MyCustomInterpolation
|
||||
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.message.init_interpolation")
|
||||
@_semantics("constant_evaluable")
|
||||
public init(stringInterpolation: MyCustomInterpolation) {
|
||||
var s = stringInterpolation
|
||||
s.appendLiteral("\n")
|
||||
self.interpolation = s
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@_optimize(none)
|
||||
@_semantics("oslog.message.init_stringliteral")
|
||||
@_semantics("constant_evaluable")
|
||||
public init(stringLiteral value: String) {
|
||||
var s = MyCustomInterpolation(
|
||||
literalCapacity: 1,
|
||||
interpolationCount: 0
|
||||
)
|
||||
s.appendLiteral(value)
|
||||
s.appendLiteral("\n")
|
||||
self.interpolation = s
|
||||
}
|
||||
}
|
||||
|
||||
@_semantics("oslog.requires_constant_arguments")
|
||||
@inlinable
|
||||
@_transparent
|
||||
@_alwaysEmitIntoClient
|
||||
@_optimize(none)
|
||||
public func xprint(_ message: MyCustomMessage) {
|
||||
let formatString = message.interpolation.formatString
|
||||
let formatStringPointer = _getGlobalStringTablePointer(formatString)
|
||||
print(String(cString: formatStringPointer))
|
||||
for a in message.interpolation.arguments.argumentClosures {
|
||||
let s = a()
|
||||
print(s)
|
||||
}
|
||||
}
|
||||
19
test/embedded/compeval-print2.swift
Normal file
19
test/embedded/compeval-print2.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
|
||||
// RUN: %target-swift-frontend -O -emit-module -o %t/MyCustomMessage.swiftmodule %S/Inputs/MyCustomMessage2.swift -enable-experimental-feature Embedded -parse-as-library
|
||||
// RUN: %target-swift-frontend -O -c -I %t %s -enable-experimental-feature Embedded -o %t/a.o
|
||||
// RUN: %target-clang %t/a.o -o %t/a.out
|
||||
// RUN: %target-run %t/a.out | %FileCheck %s
|
||||
|
||||
// REQUIRES: swift_in_compiler
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: optimized_stdlib
|
||||
// REQUIRES: OS=macosx || OS=linux-gnu
|
||||
// REQUIRES: swift_feature_Embedded
|
||||
|
||||
import MyCustomMessage
|
||||
|
||||
xprint("xprint \(42) \("my string")")
|
||||
// CHECK: xprint %lld %s
|
||||
// CHECK: 42
|
||||
// CHECK: my string
|
||||
Reference in New Issue
Block a user