mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
331 lines
10 KiB
Swift
331 lines
10 KiB
Swift
// RUN: %target-typecheck-verify-swift -swift-version 5
|
|
|
|
// Tests for the diagnostics emitted in Sema for checking constantness of
|
|
// function arguments annotated to be so. Note that these annotation are
|
|
// specific to the new os log overlay and the low-level atomics library.
|
|
|
|
// The following tests check different types of constants that are accepted
|
|
// by the constantness Sema check. It creates helper functions with the
|
|
// semnatics annotation: "oslog.requires_constant_arguments" which requires that
|
|
// all arguments passed to the function are constants. The annotation is meant
|
|
// to be used only by the os log overlay. The test helpers use it here only for
|
|
// the purpose of testing the functionality.
|
|
|
|
// Check simple literals.
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func constantArgumentFunction<T>(_ constArg: T) {
|
|
}
|
|
|
|
func literalTest(x: Int) {
|
|
constantArgumentFunction(1)
|
|
constantArgumentFunction("Some string")
|
|
constantArgumentFunction(1.9)
|
|
constantArgumentFunction(true)
|
|
constantArgumentFunction(x)
|
|
// expected-error@-1 {{argument must be an integer literal}}
|
|
constantArgumentFunction(x + 2)
|
|
// expected-error@-1 {{argument must be an integer literal}}
|
|
}
|
|
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func constArgFunctionWithReturn<T>(_ constArg: T) -> T {
|
|
return constArg
|
|
}
|
|
|
|
func testConstArgFuncWithReturn(x: Int, str: String) -> Int {
|
|
_ = constArgFunctionWithReturn("")
|
|
_ = constArgFunctionWithReturn(str)
|
|
// expected-error@-1 {{argument must be a string literal}}
|
|
constArgFunctionWithReturn(10)
|
|
// expected-warning@-1 {{result of call to 'constArgFunctionWithReturn' is unused}}
|
|
return constArgFunctionWithReturn(x)
|
|
// expected-error@-1 {{argument must be an integer literal}}
|
|
}
|
|
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func constantOptionalArgument(_ constArg: Optional<Int>) {
|
|
}
|
|
|
|
// Correct test cases.
|
|
func optionalTest(x: Int) {
|
|
constantOptionalArgument(nil)
|
|
constantOptionalArgument(0)
|
|
constantArgumentFunction(x + 2)
|
|
// expected-error@-1 {{argument must be an integer literal}}
|
|
}
|
|
|
|
// Test string interpolation literals. We can only enforce constantness on custom string
|
|
// interpolation types. For string types, the constant is a string literal.
|
|
|
|
struct CustomStringInterpolation : ExpressibleByStringLiteral,
|
|
ExpressibleByStringInterpolation {
|
|
struct StringInterpolation : StringInterpolationProtocol {
|
|
init(literalCapacity: Int, interpolationCount: Int) { }
|
|
mutating func appendLiteral(_ x: String) { }
|
|
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
mutating func appendInterpolation(_ x: Int) { }
|
|
}
|
|
init(stringLiteral value: String) { }
|
|
init(stringInterpolation: StringInterpolation) { }
|
|
}
|
|
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func constantStringInterpolation(_ constArg: CustomStringInterpolation) {}
|
|
|
|
func testStringInterpolationLiteral(x: Int) {
|
|
constantStringInterpolation("a string interpolation literal \(x)")
|
|
// expected-error@-1 {{argument must be an integer literal}}
|
|
constantStringInterpolation("a string interpolation literal \(10)")
|
|
}
|
|
|
|
// Test multiple arguments.
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func multipleArguments(_ arg1: Int, _ arg2: Bool, _ arg3: String, _ arg4: Double) {
|
|
}
|
|
|
|
func testMultipleArguments(_ x: String, _ y: Double) {
|
|
multipleArguments(56, false, "", 23.3)
|
|
multipleArguments(56, false, x, y)
|
|
// expected-error@-1 {{argument must be a string literal}}
|
|
// expected-error@-2 {{argument must be a floating-point literal}}
|
|
}
|
|
|
|
// Test enum uses.
|
|
enum Color {
|
|
case red
|
|
case blue
|
|
case green
|
|
case rgb(r: Int, g: Int, b: Int)
|
|
}
|
|
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func enumArgument(_ color: Color) { }
|
|
|
|
func testEnumArgument(r: Int, c: Color) {
|
|
enumArgument(.rgb(r: 12, g: 0, b: 1))
|
|
enumArgument(.green)
|
|
enumArgument(.rgb(r: r, g: 200, b: 453))
|
|
// expected-error@-1 {{argument must be an integer literal}}
|
|
enumArgument(c)
|
|
// expected-error@-1 {{argument must be a case of enum 'Color'}}
|
|
}
|
|
|
|
// Test type expressions.
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func typeArgument<T>(_ t: T.Type) { }
|
|
|
|
func testTypeArgument<S>(_ t: S.Type) {
|
|
typeArgument(Int.self)
|
|
typeArgument(S.self)
|
|
typeArgument(t)
|
|
// expected-error@-1 {{argument must be a <Type>.self}}
|
|
}
|
|
|
|
// Test constant evaluable function calls.
|
|
@_semantics("constant_evaluable")
|
|
func constantEval(_ x: Int, _ y: Bool) -> Int { x + 100 }
|
|
|
|
func testConstantEvalArgument(x: Int) {
|
|
constantArgumentFunction(constantEval(90, true))
|
|
constantArgumentFunction(constantEval(constantEval(500, true), false))
|
|
constantArgumentFunction(constantEval(x, true))
|
|
// expected-error@-1 {{argument must be an integer literal}}
|
|
}
|
|
|
|
// Test constant evaluable function calls with default arguments.
|
|
@_semantics("constant_evaluable")
|
|
func constantEvalAdvanced(_ x: () -> Int, _ y: Bool = true, z: String) { }
|
|
|
|
func testConstantEvalAdvanced(arg: Int) {
|
|
constantArgumentFunction(constantEvalAdvanced({ arg }, z: ""))
|
|
}
|
|
|
|
// Test constant evaluable methods.
|
|
struct E {
|
|
// expected-note@-1 {{'E' declared here}}
|
|
func constantEvalMethod1() -> E { return self }
|
|
|
|
@_semantics("constant_evaluable")
|
|
func constantEvalMethod2() -> E { return self }
|
|
|
|
@_semantics("constant_evaluable")
|
|
static func constantEvalMethod3(x: Bool) -> E { return E() }
|
|
|
|
@_semantics("constant_evaluable")
|
|
static func constantEvalMethod4() -> E { return E() }
|
|
}
|
|
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func functionNeedingConstE(_ x: E) { }
|
|
|
|
func testConstantEvalMethod(b: Bool) {
|
|
functionNeedingConstE(E().constantEvalMethod1())
|
|
// expected-error@-1 {{argument must be a static method or property of 'E'}}
|
|
functionNeedingConstE(E().constantEvalMethod2())
|
|
functionNeedingConstE(.constantEvalMethod3(x: true))
|
|
functionNeedingConstE(.constantEvalMethod3(x: b))
|
|
// expected-error@-1 {{argument must be a bool literal}}
|
|
functionNeedingConstE(.constantEvalMethod4())
|
|
}
|
|
|
|
// Test functions with autoclosures.
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func autoClosureArgument(_ number: @autoclosure @escaping () -> Int) { }
|
|
|
|
func testAutoClosure(_ number: Int) {
|
|
autoClosureArgument(number)
|
|
}
|
|
|
|
@_semantics("constant_evaluable")
|
|
func constEvalWithAutoClosure(_ number: @autoclosure @escaping () -> Int) -> Int {
|
|
return 0
|
|
}
|
|
|
|
func testConstantEvalAutoClosure(_ number: Int) {
|
|
constantArgumentFunction(constEvalWithAutoClosure(number))
|
|
}
|
|
|
|
// Test nested use of constant parameter.
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func testConstantArgumentRequirementPropagation(constParam: Int) {
|
|
constantArgumentFunction(constParam)
|
|
}
|
|
|
|
// Test nested use of constant parameter in constant evaluable function.
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func testConstantArgumentWithConstEval(constParam: Int) {
|
|
constantArgumentFunction(constantEval(constParam, true))
|
|
}
|
|
|
|
// Test parital-apply of constantArgumentFunction.
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func constArg2(_ x: Int) -> Int { x }
|
|
|
|
// This is not an error.
|
|
func testPartialApply() -> ((Int) -> Int) {
|
|
return constArg2
|
|
}
|
|
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func constArg3(_ x: (Int) -> Int) -> Int { x(0) }
|
|
|
|
@_semantics("constant_evaluable")
|
|
func intIdentity(_ x: Int) -> Int { x }
|
|
|
|
func testPartialApply2() -> Int {
|
|
return constArg3(intIdentity)
|
|
// expected-error@-1 {{argument must be a closure}}
|
|
}
|
|
|
|
// Test struct and class constructions. Structs whose initializers are marked as
|
|
// constant_evaluable are considered as constants.
|
|
|
|
struct AStruct {
|
|
var i: Int
|
|
}
|
|
|
|
struct BStruct {
|
|
var i: Int
|
|
@_semantics("constant_evaluable")
|
|
init(_ value: Int) {
|
|
i = value
|
|
}
|
|
}
|
|
|
|
class CClass {
|
|
var str: String
|
|
init() {
|
|
str = ""
|
|
}
|
|
}
|
|
|
|
func testStructAndClasses(arg: Int) {
|
|
constantArgumentFunction(AStruct(i: 9))
|
|
// expected-error@-1 {{argument must be a static method or property of 'AStruct'}}
|
|
constantArgumentFunction(BStruct(340))
|
|
constantArgumentFunction(CClass())
|
|
// expected-error@-1 {{argument must be a static method or property of 'CClass'}}
|
|
}
|
|
|
|
// Test "requires_constant" annotation on protocol requirements.
|
|
protocol Proto {
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func method(arg1: Int, arg2: Bool)
|
|
}
|
|
|
|
struct SConf : Proto {
|
|
func method(arg1: Int, arg2: Bool) { }
|
|
}
|
|
|
|
func testProtocolMethods<T: Proto>(b: Bool, p: T, p2: Proto, s: SConf) {
|
|
p.method(arg1: 6, arg2: true)
|
|
p.method(arg1: 6, arg2: b)
|
|
// expected-error@-1 {{argument must be a bool literal}}
|
|
p2.method(arg1: 6, arg2: b)
|
|
// expected-error@-1 {{argument must be a bool literal}}
|
|
// Note that even though 's' conforms to Proto, since its method is not
|
|
// annotated as requiring constant arg2, there will be no error here.
|
|
s.method(arg1: 6, arg2: b)
|
|
}
|
|
|
|
// Check requiers annotation on a class method.
|
|
class ClassD {
|
|
@_semantics("oslog.requires_constant_arguments")
|
|
func method(_ arg1: Int, _ arg2: Bool)
|
|
}
|
|
|
|
func testClassMethod(d: ClassD, b: Bool) {
|
|
d.method(10, true)
|
|
d.method(10, b)
|
|
// expected-error@-1 {{argument must be a bool literal}}
|
|
}
|
|
|
|
// Test that the check is resilient to errors in the semantics attribute.
|
|
@_semantics("oslog.requires_constant_")
|
|
func funcWithWrongSemantics(x: Int) {}
|
|
|
|
func testFunctionWithWrongSemantics(x: Int) {
|
|
funcWithWrongSemantics(x: x)
|
|
}
|
|
|
|
// Test that the check is resilient to other type errors.
|
|
func testOtherTypeErrors() {
|
|
constantArgumentFunction(x)
|
|
// expected-error@-1 {{use of unresolved identifier 'x'}}
|
|
constantArgumentFunction(10 as String)
|
|
// expected-error@-1 {{cannot convert value of type 'Int' to type 'String' in coercion}}
|
|
}
|
|
|
|
// Test constantness of the ordering used in the atomic operations. The atomic
|
|
// operations that requires a constant ordering are required to use the
|
|
// semantics annotation "atomics.requires_constant_orderings".
|
|
|
|
internal struct AtomicLoadOrdering {
|
|
@_semantics("constant_evaluable")
|
|
internal static var acquiring: Self { Self() }
|
|
|
|
@_semantics("constant_evaluable")
|
|
internal static var sequentiallyConsistent: Self { Self() }
|
|
}
|
|
|
|
internal struct UnsafeAtomicIntStub {
|
|
@_semantics("atomics.requires_constant_orderings")
|
|
internal func load(
|
|
ordering: AtomicLoadOrdering = .sequentiallyConsistent
|
|
) -> Int {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func testAtomicOrderingConstantness(
|
|
atomicInt: UnsafeAtomicIntStub,
|
|
myOrder: AtomicLoadOrdering
|
|
) {
|
|
_ = atomicInt.load()
|
|
_ = atomicInt.load(ordering: .acquiring)
|
|
_ = atomicInt.load(ordering: .sequentiallyConsistent)
|
|
_ = atomicInt.load(ordering: myOrder)
|
|
// expected-error@-1 {{ordering argument must be a static method or property of 'AtomicLoadOrdering'}}
|
|
}
|