属性#
Attributes are annotations placed before structures in source code. They take the form #attribute(...).
An attribute occupies the entire line, and newlines are not allowed within it.
Attributes do not normally affect the meaning of programs. Unused attributes will be reported as warnings.
属性的语法如下所示:
attribute ::= '#' attribute-name
| '#' attribute-name '(' attribute-arguments ')'
attribute-name ::= LIDENT | LIDENT '.' LIDENT
attribute-arguments ::= attribute-argument (',' attribute-argument )*
attribute-argument ::= expr | LIDENT '=' expr
expr ::= LIDENT | UIDENT | STRING | 'true' | 'false'
| LIDENT '.' LIDENT
| LIDENT '(' attribute-arguments ')'
| LIDENT '.' LIDENT '(' attribute-arguments ')'
属性有两种类别:内建属性和用户自定义属性。例如:
#deprecated("message")
#custom.attribute(key="value", flag=true)
第一个属性是一个内建的属性,在属性名中,它没有命名空间前缀。内建属性会被 MoonBit 编译器检查,并且有特殊的含义。
第二个属性是用户自定义属性,它的名字中包含命名空间前缀。用户自定义属性会被编译器忽略,通过解析源码,它能够被外部的工具使用。
备注
MoonBit 在设计上有意不支持运行时反射。运行时反射容易被滥用,使得工具链(例如编译器)在编译时不可能捕获到错误,让代码的维护变得更困难。它也会对性能优化产生负面影响。
我们更倾向于使用编译时的代码生成,维持静态类型分析和性能上的优势(代码生成也需要节约地使用,避免不必要的复杂性)。
deprecated 属性#
The #deprecated attribute is used to mark an API as deprecated. MoonBit emits
a warning when the deprecated API is used, and if the API is listed in completion,
it will be shown with a strikethrough style. For example:
#deprecated
pub fn foo() -> Unit {
...
}
#deprecated("Use Bar2 instead")
pub(all) enum Bar {
Ctor1
Ctor2
}
#deprecated 属性能够在如下上下文中使用:
顶层的值声明(包括
fn,let和const)顶层的类型声明(包括
type,struct和enum)特征声明
特征的默认实现
extend声明
常见形式包括:
#deprecated将这个项目标记成弃用的,并使用默认的警告信息。
#deprecated("使用 new_function 代替")将 API 标记为弃用,并自定义一个警告信息。每当这个弃用的 API 被使用时,自定义信息将会显示在警告中。
#deprecated("使用 new_function 代替", skip_current_package=true)将 API 标记为弃用的同时自定义警告信息,但是当 API 在同一个包内使用时不触发警告。
#deprecated(skip_current_package=true)Marks the item as deprecated with a default warning message, but skips emitting warnings within the same package. When both a message and
skip_current_packageare present, either argument order is accepted.
Alert Attribute#
#alert 属性会为 API 附加一个类别和一条消息。当代码使用该 API 时,MoonBit 会发出 alert 警告。
#alert(unsafe, "This function is unsafe.")
fn[A] alert_unsafe_get(arr : Array[A], index : Int32) -> A {
arr[index]
}
第一个参数是 alert 类别,第二个参数是展示给用户的消息。该警告可以通过 alert、alert_unsafe 等警告名称配置。
For more detail, see alert warning.
alias 属性#
alias 属性用于重载一个和索引操作相关的运算符,或者为一个顶层函数与变量创建别名。它有两种形式:
#alias("op"):op是下面其中一个字符串,表示对应的索引操作:_[_]:数组索引操作符_[_]=_:数组索引赋值操作符_[_:_]:op_as_view 操作符
#alias(id): 其中id是一个代表别名的标识符
所有形式都支持额外的参数:
visibility="modifier"一个带标签的参数,用于改变别名的可见性。
modifier可以是pub或者priv,如果没有声明,别名会和原始的函数或者变量有一样的可见性。deprecated或者deprecated="message"将别名标记为弃用的。如果提供了
message信息,当别名被使用时,信息会被一起显示在警告中。
#alias 不能用于 declare fn 或 declare let。请将该属性放在对应的定义上。
若要在基于声明的接口中暴露别名,请用 declare fn 或 declare let 单独声明别名。声明指定名称及其类型,但不要求这些名称之间存在别名关系。实现使用 #alias 还是分别提供定义,属于实现细节。
///|
declare pub fn new_name() -> Int32
///|
declare pub fn old_name() -> Int32
///|
#alias(old_name)
pub fn new_name() -> Int32 {
42
}
To graceful migration from old API to new API, you can rename the old API directly, and create an alias with the old name, mark it as deprecated. For example:
#alias(old_name, deprecated)
fn new_name() -> Unit {
()
}
label_migration 属性#
The #label_migration attribute is used to help you safely evolve your API
by warning users during the transition period.
它有如下三种形式:
#label_migration(id, fill=true, msg="message")The
fillargument is used when you want to refactor an optional parameter. You can usefill=truewhen you want to eventually make an optional parameter required. You can usefill=falsewhen you want to eventually remove an optional parameter.msg参数是一个字符串,用于提供额外的信息,告诉使用者关于可见性的变更。#label_migration(x, fill=true) #label_migration(y, fill=false) fn label_migration_fill(x~ : Int32 = 0, y~ : Int32 = 1) -> Int32 { x + y }
#label_migration(id, allow_positional=true, msg="message")The
allow_positionalargument is used when you want a labelled parameter to be used without its label being provided. When the parameter is used positionally (without a label), the compiler reports a warning. This is useful when you want to change a positional parameter to a labelled parameter without breaking the downstream code.msg参数是一个字符串,用于提供额外的信息,告诉使用者关于可见性的变更。#label_migration(x, allow_positional=true) fn label_migration_allow_positional(x~ : Int32) -> Int32 { x }
#label_migration(id, alias=new_id, msg="message")The alias argument allows you to provide an alternative name to a labelled parameter. This is useful when renaming a parameter to maintain backward compatibility. If a warning message is provided, the compiler warns when using the alias; otherwise, the alias can be used without warnings.
msg参数是一个字符串,用于提供额外的信息,告诉使用者关于可见性的变更。#label_migration(x, alias=xx) #label_migration(x, alias=y, msg="warning") fn label_migration_alias(x~ : Int32) -> Int32 { x }
visibility 属性#
备注
这里的文档不涉及访问控制。关于如何使用 pub、pub(all) 和 priv,见访问控制。
The #visibility attribute is similar to the #deprecated attribute, but it is used to hint that a type will change its visibility in the future.
For outside usages, if the usage will be invalidated by the visibility change in future, a warning will be emitted.
// in @util package
#visibility(change_to="readonly", "Point will be readonly in the future.")
pub(all) struct Point {
x : Int32
y : Int32
}
#visibility(change_to="abstract", "Use new_text and new_binary instead.")
pub(all) enum Resource {
Text(String)
Binary(Bytes)
}
pub fn new_text(str : String) -> Resource {
...
}
pub fn new_binary(bytes : Bytes) -> Resource {
...
}
// in another package
fn main {
let p = Point::{ x: 1, y: 2 } // warning
let { x, y } = p // ok
println(p.x) // ok
match Resource::Text("") { // warning
Text(s) => ... // waning
Binary(b) => ... // warning
}
}
The #visibility attribute takes a required change_to argument and an
optional message argument.
change_to参数是一个表示该类型新的可见性的字符串,它可以是"abstract"或者"readonly""change_to"的值将要禁止的使用
"readonly"创建该类型的实例,或者变更实例的字段的值。
"abstract"创建该类型的实例、变更实例的字段的值、对它的实例进行模式匹配或者访问实例的字段。
The optional
messageargument is a string that provides additional information about the visibility change.
非穷尽枚举属性#
#non_exhaustive 属性用于标记定义包可能在未来版本中添加构造器的 enum。它只能用于枚举声明。
///|
#non_exhaustive
pub(all) enum Response {
Ok(String)
NotFound
}
定义包之外的代码必须使用 Type::.. 模式处理未来可能出现的构造器:
///|
pub fn render(response : @base.Response) -> String {
match response {
@base.Ok(body) => body
@base.NotFound => "not found"
@base.Response::.. => "unsupported response"
}
}
Type::.. 模式专用于前向兼容。它不能替代枚举中当前已知的构造器:这些构造器必须在该模式之前显式匹配。处理定义包未来版本新增的构造器时,通配符模式(_)不能替代 Type::..。
因此,只要下游匹配使用了 Type::..,为 #non_exhaustive 枚举新增构造器就是兼容变更。
internal 属性#
The #internal attribute is used to mark a function, type, or trait as internal.
Any usage of the internal function or type in other modules will emit an alert warning.
#internal(unsafe, "This is an unsafe function")
fn[A] internal_unsafe_get(arr : Array[A], index : Int32) -> A {
arr[index]
}
The internal attribute takes a required category argument and an optional
message argument. category is a identifier that indicates the category of
the alert, and message is a string that provides additional message for the
alert.
alert 警告可以通过设置 moon.pkg 中的 warnings 字段关闭。更多细节见 alert 警告。
warnings 属性#
The #warnings attribute is used to configure warning settings for a specific
top-level declaration. It can enable, disable or treat an enabled warning as error
for specific warnings in that declaration.
The argument is a string that specifies the warning list. It can contain multiple warning names, each prefixed with a sign:
#warnings("-unused_value")
fn warnings_example() -> Unit {
let x = 42
}
这些前缀有以下含义:
+warning_name: 启用这个警告-warning_name: 关闭这个警告@warning_name:将已启用的警告视为错误(已弃用)
@ 开关已在 v0.10.4 中弃用。请使用 + 或 - 配置单项警告;需要让警告导致 CI 构建失败时,请使用 moon check --deny-warn 或相应命令的同类选项。
目前这个属性只对部分特定的警告工作。
To learn more about warning names, see warning list.
Must Implement One 属性#
#must_implement_one 属性用于 trait,要求每个实现都显式定义至少一个方法,而不是只依赖默认方法实现。
不带参数时,必须显式实现该 trait 的至少一个方法:
#must_implement_one
pub(open) trait RequireAnyMethod {
f(Self) -> Unit = _
g(Self) -> Unit = _
}
impl RequireAnyMethod with f(_) { () }
impl RequireAnyMethod with g(_) { () }
type AnyImpl
impl RequireAnyMethod for AnyImpl with f(_) { () }
带方法名时,必须显式实现列出的方法中的至少一个:
#must_implement_one(f, g)
pub(open) trait RequireSelectedMethod {
f(Self) -> Unit = _
g(Self) -> Unit = _
h(Self) -> Unit = _
}
impl RequireSelectedMethod with f(_) { () }
impl RequireSelectedMethod with g(_) { () }
impl RequireSelectedMethod with h(_) { () }
type SelectedImpl
impl RequireSelectedMethod for SelectedImpl with g(_) { () }
同一个 trait 上可以使用多个 #must_implement_one 属性,以要求显式实现多个方法组中的方法。
Inline Attribute#
#inline 属性是函数的优化提示。它会请求编译器尽可能内联该函数:
#inline
fn add_one(x : Int32) -> Int32 {
x + 1
}
使用 #inline(never) 可以请求编译器不要内联某个函数:
#inline(never)
fn keep_stack_frame(x : Int32) -> Int32 {
x + 1
}
这些属性只是提示,不会改变函数在源码层面的行为。
external 属性#
#external属性用于标记一个抽象类型为外部类型。
对于 Wasm 和 Wasm GC 后端,它将被解释为
externref。对于 JavaScript 后端,它将被解释为
any。对于原生后端,它将被解释为
void*。
#external
type AttrPtr
borrow 和 owned 属性#
#borrow 和 #owned 属性用于 FFI 声明,用来描述带引用计数的 MoonBit 参数如何传递给外部代码。这对于 Bytes、String、FixedArray[T] 以及抽象类型等装箱的 MoonBit 值很重要;这些值在 C 和 Wasm 后端上的生命周期由引用计数管理。
当外部函数只在调用期间读取 param,且不会存储或返回它时,使用 #borrow(param)。借用参数仍由 MoonBit 拥有,因此外部函数不需要为它调用 moonbit_decref 或 $moonbit.decref。
当外部函数接管 param 的所有权时,例如将它存储起来并在之后释放,使用 #owned(param)。owned 参数最终必须由外部侧在不再需要时释放。
#borrow(filename)
extern "C" fn open(filename : Bytes, flags : Int32) -> Int32 = "open"
完整的调用约定规则见 FFI 生命周期管理。
不安全的无环属性#
#unsafe_cycle_free 属性是用于 struct、enum 或 newtype 声明的高级优化断言。它告诉编译器,该类型的值永远不会参与引用环。编译器会在分析类型是否可能成环时信任此断言,而不会验证它。
#unsafe_cycle_free
pub(all) struct CycleFreeNode {
mut next : CycleFreeNode?
}
危险
仅当程序能够保证无环不变量时才应使用此属性。错误的断言会使涉及该类型的引用环被视为不可能存在,从而无法回收。
as_free_fn 属性#
#as_free_fn 属性用于标记一个方法,同时它也被声明为一个自由函数。它还可以改变自由函数的可见性、自由函数的名字,并且提供单独的弃用警告。
#as_free_fn(dec, visibility="pub", deprecated="use `Int32::decrement` instead")
#as_free_fn(visibility="pub")
fn Int32::decrement(i : Self) -> Self {
i - 1
}
test {
let _ = decrement(10)
let _ = (10).decrement()
}
callsite 属性#
#callsite 属性用于标记发生在调用位置的属性。
它可以是 autofill,用于在调用位置自动填充参数 SourceLoc 和 ArgLoc。
skip 属性#
The #skip attribute is used to skip a single test block. It can be written as
#skip or with a reason, such as #skip("blocked by external service"). The
type checking will still be performed.
Coverage Skip Attribute#
#coverage.skip 属性会跳过函数内的覆盖率操作。
#coverage.skip
fn platform_specific_helper() -> Unit {
()
}
它适用于不应影响覆盖率报告的函数,例如特定平台的回退代码,或有意排除在覆盖率统计之外的代码路径。更多细节见跳过覆盖率。
cfg 属性#
#cfg 属性控制某个声明是否参与编译。条件在编译时求值,可以使用:
true或false:始终包含或排除某个声明。target:选择编译后端,例如target="wasm"。platform:选择 native 后端的操作系统,例如platform="windows"。not(...)、all(...)或any(...):分别表示条件取反、所有条件均成立或至少一个条件成立。
警告
目前,platform 条件也会将 target 限定为 native。例如,platform="windows" 等价于 all(target="native", platform="windows")。
对条件取反时需要注意这一点:not(platform="windows") 也会匹配所有 target 不为 native 的情况。若只想选择 Windows 以外的 native 构建,请使用 all(target="native", not(platform="windows"))。
#cfg(true)
fn cfg_true() -> Unit {
()
}
#cfg(false)
fn cfg_false() -> Unit {
()
}
#cfg(target="wasm")
fn cfg_wasm() -> Unit {
()
}
#cfg(platform="windows")
fn cfg_native_windows() -> Unit {
()
}
#cfg(not(platform="windows"))
fn cfg_except_native_windows() -> Unit {
()
}
#cfg(all(target="native", not(platform="windows")))
fn cfg_native_non_windows() -> Unit {
()
}
#cfg(any(target="wasm", target="native"))
fn cfg_any() -> Unit {
()
}
模块属性#
module 属性用于为 JavaScript 后端声明模块依赖。
在 cjs 格式中,它被解释为 require,而在 esm 格式中,它被解释为 import。
#module("math-utils")
pub extern "js" fn add_from_module(x : Int32, y : Int32) -> Int32 = "add"
导出名称属性#
#export_name 属性为使用 pkgtype(kind: "foreign_library") 声明的包中的公开函数指定稳定的导出符号名称。该名称用于生成的 Wasm、JavaScript 或 C 输出:
#export_name("add")
pub fn add_one(value : Int32) -> Int32 {
value + 1
}
MoonBit 目前要求导出名在包内唯一,并且无论选择哪个后端,都必须是有效的 C 符号标识符。
警告
已知编译器问题:#export_name 目前会对所有后端施加 C 符号标识符限制。WebAssembly 导出名是 UTF-8 字符串,并不限于 C 标识符。
该属性不能用于泛型函数、带可选参数的函数、方法或没有函数体的声明。
对于新的导出,优先使用 #export_name,而不是各后端的 exports 链接配置。它将导出名称放在函数旁,并适用于所有支持外部库输出的后端。当导出集合或名称需要因后端而异,或者无法修改源代码添加属性时,使用 exports。
导出声明的作用域是生成产物的包。依赖中的属性在该依赖自身构建为产物时生效,但不会向下游包的产物添加符号。若要暴露依赖的功能,请在执行导出的包中定义并导出一个包装函数。
备注
native 后端目前不支持将 foreign_library 包导出为可链接的库产物,包括 .dll 或 .so 等共享库。如需导出的库,请使用 Wasm 或 JavaScript。
有关各后端的替代方式,请参阅导出函数。
Proof Pure 属性#
#proof_pure 属性使无副作用的函数可同时用于可执行代码和面向证明的逻辑。
#proof_pure
fn height(t : Tree) -> Int32 {
match t {
Empty => 0
Node(_, _, _, h) => h
}
}
目前,它支持普通的顶层函数和常规方法。#proof_pure 定义暂不支持验证契约、直接递归和相互递归。
有关它在规约中的作用和完整示例,请参阅形式化验证。
Proof External 属性#
#proof_external(module, symbol) 属性指示验证降级过程使用 Why3 模块中的类型表示一个抽象 MoonBit 类型。
///|
#proof_external("set.Fset", "fset")
pub type ProofSet[T]
在本例中,面向证明的逻辑中的 ProofSet[T] 会降级为 Why3 类型 set.Fset.fset T。该属性不会实现这个 MoonBit 类型、提供运行时值,也不会将该类型连接到 FFI ABI。
模块和符号映射是可信验证边界的一部分。有关对应的操作导入和证明示例,请参阅形式化验证。
Proof Import 属性#
#proof_import(module) 属性将 .mbtp 文件中仅用于证明的逻辑声明映射到 Why3 模块中的符号。声明的字符串函数体指定 Why3 符号;它不是可执行的 MoonBit 表达式。
#proof_import("set.Fset")
fn proof_set_mem(x : Int32, set : ProofSet[Int32]) -> Bool = "mem"
#proof_import("set.Fset")
fn proof_set_empty() -> ProofSet[Int32] = "empty"
#proof_import("set.Fset")
fn proof_set_add(x : Int32, set : ProofSet[Int32]) -> ProofSet[Int32] = "add"
lemma proof_set_add_contains(x : Int32) where {
proof_ensure: proof_set_mem(
x,
proof_set_add(x, proof_set_empty()),
),
} {}
这里,set.Fset 是 Why3 模块路径,而 "mem"、"empty" 和 "add" 指定该模块中的符号。这些声明只影响验证降级过程,并不提供运行时实现或 FFI 绑定。最后的引理会产生证明义务;导入声明本身不会。
声明的签名和符号映射是可信验证边界的一部分。有关完整示例和使用说明,请参阅形式化验证。