常量求值
const 变量
const 变量是一种特殊的变量,它可以定义在编译时完成求值,并且在运行时不可改变的变量。
const 变量与 let/var 声明的变量的区别是必须在定义时就初始化,且必须用 const 表达式初始化。因此 const 变量的类型只能是 const 表达式支持的类型。
与 let/var 一样,const 变量也支持省略类型。
const 表达式见下文定义。
const a: Int64 = 0 // ok
const b: Int64 // error, b is uninitialized
const c = f() // error, f() is not const expression
const d = 0 // ok, the type of d is Int64
func f(): Unit {}
const 变量定义后可以像 let/var 声明的变量一样使用。与 let/var 声明的变量不同,const 由于在编译时就可以得到结果,可以大幅减少程序运行时需要的计算。
const a = 0
main(): Int64 {
let b: Int64 = a // ok
print(a) // ok
let c: VArray<Int64, $0> = [] // ok
return 0
}
const 变量可以是全局变量,局部变量,静态成员变量。const 变量不能在扩展中定义。
const a = 0 // ok
class C {
const b = 0 // error, const member field must be modified by static
static const c = 0 //ok
var v: Int64
init() {
const d = 0 // ok
v = b + c + d
}
}
extend C {
const e = 0 // error, const cannot be defined in extend
}
const 变量可以访问对应类型的所有实例成员,也可以调用对应类型的所有非 mut 实例成员函数。
struct Foo {
let a = 0
var b = 0
const init() {}
func f1() {}
const func f2() {}
mut func f3() {
b = 123
}
}
main(): Int64 {
const v = Foo()
print(v.a) // ok
print(v.b) // ok
v.f1() // ok
v.f2() // ok
v.f3() // error, f3 is mut function
return 0
}
const 变量初始化后该类型实例的所有成员都是 const 的(深度 const,包含成员的成员),因此不能被用于左值。
struct Foo {
let a = 0
var b = 0
const init() {}
}
func f() {
const v = Foo()
v.a = 1 // error
v.b = 1 // error
}
const 表达式
某些特定形式的表达式,被称为 const 表达式,这些表达式具备了可以在编译时求值的能力。在 const 上下文中,这些是唯一允许的表达式,并且始终会在编译时进行求值。而在其它非 const 上下文,const 表达式不保证在编译时求值。
以下表达式都是 const 表达式。
- 数值类型、Bool、Unit、Rune、String 类型的字面量(不包含插值字符串)。
- 所有元素都是 const 表达式的 array 字面量(不能是
Array
类型),tuple 字面量。 - const 变量,const 函数形参,const 函数中的局部变量。
- const 函数,包含使用 const 声明的函数名、符合 const 函数要求的 lambda、以及这些函数返回的函数表达式。
- const 函数调用(包含 const 构造函数),该函数的表达式必须是 const 表达式,所有实参必须都是 const 表达式。
- 所有参数都是 const 表达式的 enum 构造器调用,和无参数的 enum 构造器。
- 数值类型、Bool、Unit、Rune、String 类型的算数表达式、关系表达式、位运算表达式,所有操作数都必须是 const 表达式。
- if、match、try、控制转移表达式(包含 return、break、continue、throw)、is、as。这些表达式内的表达式必须都是 const 表达式。
- const 表达式的成员访问(不包含属性的访问),tuple 的索引访问。
- const init 和 const 函数中的 this 和 super 表达式。
- const 表达式的 const 实例成员函数调用,且所有实参必须都是 const 表达式。
const 上下文
const 上下文是一类特定上下文,在这些上下文内的表达式都必须是 const 表达式,并且这些表达式始终在编译时求值。
const 上下文是指 const 变量初始化表达式。
const 函数
const 函数是一类特殊的函数,这些函数具备了可以在编译时求值的能力。在 const 上下文中调用这种函数时,这些函数会在编译时执行计算。而在其它非 const 上下文,const 函数会和普通函数一样在运行时执行。
const 函数与普通函数的区别是限制了部分影响编译时求值的功能,const 函数中只能出现声明、const 表达式、受限的部分赋值表达式。
- const 函数声明必须使用 const 修饰。
- 全局 const 函数和 static const 函数中只能访问 const 声明的外部变量,包含 const 全局变量、const 静态成员变量,其它外部变量都不可访问。const init 函数和 const 实例成员函数除了能访问 const 声明的外部变量,还可以访问当前类型的实例成员变量。
- const 函数中的表达式都必须是 const 表达式,const init 函数除外。
- const 函数中可以使用 let、const 声明新的局部变量。但不支持 var。
- const 函数中的参数类型和返回类型没有特殊规定。如果该函数调用的实参不符合 const 表达式要求,那这个函数调用不能作为 const 表达式使用,但仍然可以作为普通表达式使用。
- const 函数不一定都会在编译时执行,例如可以在非 const 函数中运行时调用。
- const 函数与非 const 函数重载规则一致。
- 数值类型、Bool、Unit、Rune、String 类型 和 enum 支持定义 const 实例成员函数。
- 对于 struct 和 class,只有定义了 const init 才能定义 const 实例成员函数。class 中的 const 实例成员函数不能是 open 的。struct 中的 const 实例成员函数不能是 mut 的。
const func f(a: Int64): Int64 { // ok
let b = 6
if (a > 5 && (b + a) < 15 ) {
return a * a
}
return a
}
class A {
var a = 0
}
const func f1(a: A): Unit { // ok
return
}
const func f2(): A {
return A() // error, A did not contains const init
}
接口中的 const 函数
接口中也可以定义 const 函数,但会受到以下规则限制。
- 接口中的 const 函数,实现类型必须也用 const 函数才算实现接口。
- 接口中的非 const 函数,实现类型使用 const 或非 const 函数都算实现接口。
- 接口中的 const 函数与接口的 static 函数一样,只有在该接口作为泛型约束的时候,受约束的泛型变元或变量才能使用这些 const 函数。
interface I {
const func f(): Int64
const static func f2(): Int64
}
const func g<T>(i: T) where T <: I {
return i.f() + T.f2()
}
const init
有无 const init 决定了哪些自定义 struct/class 可以用在 const 表达式上。一个类型能否定义 const init 取决于以下条件。
- 如果当前类型是 class,则不能具有 var 声明的实例成员变量,否则不允许定义 const init。如果当前类型具有父类,当前的 const init 必须调用父类的 const init(可以显式调用或者隐式调用无参 const init),如果父类没有 const init 则报错。
- Object 类型的无参 init 也是 const init,因此 Object 的子类可以使用该 const init。
- 当前类型的实例成员变量如果有初始值,初始值必须要是 const 表达式,否则不允许定义 const init。
- const init 内可以使用赋值表达式对实例成员变量赋值,除此以外不能有其它赋值表达式。
const init 与 const 函数的区别是 const init 内允许对实例成员变量进行赋值(需要使用赋值表达式)
struct R1 {
var a: Int64
let b: Int64
const init() { // ok
a = 0
b = 0
}
}
struct R2 {
var a = 0
let b = 0
const init() {} // ok
}
func zero(): Int64 {
return 0
}
struct R3 {
let a = zero()
const init() {} // error,Initialization of a is not const expression
}
class C1 {
var a = 0
const init() {} // error,a can not be var binding
}
struct R4 {
var a = C1()
const init() {} // error,Initialization of a is not const expression
}
open class C2 {
let a = 0
let b = R2()
const init() {} // ok
}
class C3 <: C2 {
let c = 0
const init() {} // ok
}