Skip to content

Commit 0b68336

Browse files
committed
doc: add more info about attributes
1 parent ffc830f commit 0b68336

File tree

3 files changed

+270
-130
lines changed

3 files changed

+270
-130
lines changed

next/language/attributes.md

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
# Attribute
2+
3+
Attributes are annotations placed before structures in source code. They take the form `#attribute(...)`.
4+
An attribute occupies the entire line, and newlines are not allowed within it.
5+
Attributes do not normally affect the meaning of programs. Unused attributes will be reported as warnings.
6+
7+
The syntax of attributes is defined as follows:
8+
9+
```
10+
attribute ::= '#' attribute-name
11+
| '#' attribute-name '(' properties ')'
12+
13+
attribute-name ::= LIDENT | LIDENT '.' LIDENT
14+
15+
properties ::= property (',' property)*
16+
17+
property ::= expr | LIDENT '=' expr
18+
19+
expr ::= LIDENT | UIDENT | STRING | 'true' | 'false'
20+
| LIDENT '.' LIDENT
21+
| LIDENT '(' properties ')'
22+
| LIDENT '.' LIDENT '(' properties ')'
23+
```
24+
25+
The attributes has two forms: the built-in attributes and user-defined attributes.
26+
Built-in attributes are recognized by the MoonBit compiler and have specific meanings.
27+
User-defined attributes are ignored by the compiler, but can be used by external tools,
28+
via parsing the source code.
29+
30+
```{note}
31+
MoonBit is designed not to support runtime reflection. It's easy to abuse, making it impossible for toolchains (e.g., the compiler) to catch errors at compile time, which makes code harder to maintain. It also negatively impacts performance optimization.
32+
33+
We perfer to use compile-time code generation, keeping the benefits of static typing and performance (should also be used judiciously to avoid unnecessary complexity).
34+
```
35+
36+
## Deprecated Attribute
37+
38+
The `#deprecated` attribute is used to mark an API as deprecated. MoonBit emits
39+
a warning when the deprecated API is used, and if the API is listed in completion,
40+
it will be shown with a strikethrough style. For example:
41+
42+
```{literalinclude} /sources/language/src/attributes/top.mbt
43+
:language: moonbit
44+
:start-after: start deprecated
45+
:end-before: end deprecated
46+
```
47+
48+
The `#deprecated` attribute can be used in the following contexts:
49+
50+
- Top-level value declarations (including `fn`, `let`, and `const`)
51+
- Top-level type declarations (including `type`, `struct`, and `enum`)
52+
- Trait method declarations
53+
- Trait default implementations
54+
55+
It has three forms:
56+
57+
- `#deprecated`
58+
59+
Marks the item as deprecated with a default warning message.
60+
61+
- `#deprecated("Use new_function instead")`
62+
63+
Marks the item as deprecated with a custom warning message. Every time the deprecated API is used, the provided message will be displayed as a warning.
64+
65+
- `#deprecated("Use new_function instead", skip_current_package=true)`
66+
67+
Marks the item as deprecated with a custom warning message, but skips emitting warnings when the deprecated API is used within the same package.
68+
69+
## Alias Attribute
70+
71+
The `alias` attribute is used to overload operators related to indexing, or to create an alias name for a top-level function or variable. It has two forms:
72+
73+
74+
- `#alias("op")`: where `op` is one of the following strings representing the indexing operators:
75+
76+
- `_[_]`: for the indexing operator
77+
- `_[_]=_` for the indexing assignment operator
78+
- `_[_:_]` for the as view operator
79+
80+
- `#alias(id)`: where `id` is a identifier representing the alias name.
81+
82+
Both forms allowed additional properties:
83+
84+
- `visibility="modifier"`
85+
86+
A labeled property, changes the visibility of the alias. The `modifier` can be `pub` or `priv`. If not specified, the alias will have the same visibility as the original function or variable.
87+
88+
- `deprecated` or `deprecated="message"`
89+
90+
Marks the alias as deprecated. If a message is provided, it will be displayed as a warning when the alias is used.
91+
92+
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
93+
example:
94+
95+
```{code-block} moonbit
96+
:class: top-level
97+
#alias("old_name", deprecated)
98+
fn new_name() -> Unit {
99+
...
100+
}
101+
```
102+
103+
## `label_migration` Attribute
104+
105+
The `#label_migration` attribute is used to help you safely evolve your API
106+
by warning users during the transition period.
107+
108+
It has three following forms:
109+
110+
- `#label_migration(id, fill=true, msg="message")`
111+
112+
The `fill` property is used when you want to refactor an optional parameter.
113+
You can use `fill=true` when you want to eventually make an optional
114+
parameter required. You can use `fill=false` when you want to eventually
115+
remove an optional parameter.
116+
117+
The `msg` property is an string that provides additional information about the migration.
118+
119+
```{code-block} moonbit
120+
:class: top-level
121+
#label_migration(x, fill=true)
122+
#label_migration(y, fill=false)
123+
fn f(x~: Int = 0, y~: Int = 1) -> Unit { ... }
124+
fn main {
125+
f(x=1, y=1) // warn on y being filled
126+
f() // warn on x not being filled
127+
}
128+
```
129+
130+
- `#label_migration(id, allow_positional=true, msg="message")`
131+
132+
The `allow_positional` property is used when you want a labelled parameter to be
133+
used without its label being provided. When the parameter is used positionally
134+
(without a label), the compiler reports a warning. This is useful when you want to change a positional parameter
135+
to a labelled parameter without breaking the downstream code.
136+
137+
The `msg` property is an string that provides additional information about the migration.
138+
139+
```{code-block} moonbit
140+
:class: top-level
141+
#label_migration(x, allow_positional=true)
142+
fn f(x~: Int) -> Unit { ... }
143+
144+
fn main {
145+
f(42) // warn on positional argument 42 used without label
146+
}
147+
```
148+
149+
- `#label_migration(id, alias=new_id, msg="message")`
150+
151+
The alias property allows you to provide an alternative name to a labelled
152+
parameter. This is useful when renaming a parameter to maintain backward
153+
compatibility. If a warning message is provided, the compiler warns when
154+
using the alias; otherwise, the alias can be used without warnings.
155+
156+
The `msg` property is an string that provides additional information about the migration.
157+
158+
```{code-block} moonbit
159+
:class: top-level
160+
#label_migration(x, alias=xx)
161+
#label_migration(x, alias=y, msg="warning")
162+
fn f(x~: Int) -> Unit { ... }
163+
164+
fn main {
165+
f(xx=42) // no warning
166+
f(y=42) // warning
167+
}
168+
```
169+
170+
## Visibility Attribute
171+
172+
```{note}
173+
This topic does not covered the access control. To lean more about `pub`, `pub(all)` and `priv`, see [Access Control](./packages.md#access-control).
174+
```
175+
176+
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.
177+
For outside usages, if the usage will be invalidated by the visibility change in future, a warning will be emitted.
178+
179+
```{literalinclude} /sources/language/src/attributes/top.mbt
180+
:language: moonbit
181+
:start-after: start visibility
182+
:end-before: end visibility
183+
```
184+
185+
The `#visibility` attribute takes two arguments: `change_to` and `message`.
186+
187+
- The `change_to` argument is a string that indicates the new visibility of the type. It can be either `"abstract"` or `"readonly"`.
188+
189+
| `change_to` | Invalidated Usages |
190+
|-------------|--------------------|
191+
| `"readonly"` | Creating an instance of the type or mutating the fields of the instance. |
192+
| `"abstract"` | Creating an instance of the type, mutating the fields of the instance, pattern matching, or accessing fields by label. |
193+
194+
- The `message` argument is a string that provides additional information about the visibility change.
195+
196+
## Internal Attribute
197+
198+
The `#internal` attribute is used to mark a function, type, or trait as internal.
199+
Any usage of the internal function or type in other modules will emit an alert warning.
200+
201+
```{code-block} moonbit
202+
:class: top-level
203+
#internal(unsafe, "This is an unsafe function")
204+
fn unsafe_get[A](arr : Array[A]) -> A {
205+
...
206+
}
207+
```
208+
209+
The internal attribute takes two arguments: `category` and `message`.
210+
`category` is a identifier that indicates the category of the alert, and `message` is a string that provides additional message for the alert.
211+
212+
The alert warnings can be turn off by setting the `warn-list` in `moon.pkg.json`.
213+
For more detail, see [Alert](../toolchain/moon/package.md#alert-list).
214+
215+
## External Attribute
216+
217+
The `#external` attribute is used to mark an abstract type as external type.
218+
219+
- For Wasm(GC) backends, it would be interpreted as `anyref`.
220+
- For JavaScript backend, it would be interpreted as `any`.
221+
- For native backends, it would be interpreted as `void*`.
222+
223+
```{code-block} moonbit
224+
:class: top-level
225+
#external
226+
type Ptr
227+
```
228+
229+
## Borrow and Owned Attribute
230+
231+
The `#borrow` and `#owned` attribute is used to indicate that a FFI takes ownership of its arguments. For more detail, see [FFI](./ffi.md#the-borrow-attribute).
232+
233+
## `as_free_fn` Attribute
234+
235+
The `#as_free_fn` attribute is used to mark a method that it is declared as a free function as well.
236+
It can also change the visibility of the free function, the name of the free function, and provide separate deprecation warning.
237+
238+
```{literalinclude} /sources/language/src/misc/top.mbt
239+
:language: moonbit
240+
:start-after: start as_free_fn 1
241+
:end-before: end as_free_fn 1
242+
```
243+
244+
## Callsite Attribute
245+
246+
The `#callsite` attribute is used to mark properties that happen at callsite.
247+
248+
It could be `autofill`, which is to autofill the arguments [SourceLoc and ArgLoc](/language/fundamentals.md#autofill-arguments)
249+
at callsite.
250+
251+
252+
## Skip Attribute
253+
254+
The `#skip` attribute is used to skip a single test block. The type checking will still be performed.
255+
256+
## Configuration attribute
257+
258+
The `#cfg` attribute is used to perform conditional compilation. Examples are:
259+
260+
<!-- MANUAL CHECK -->
261+
262+
```moonbit
263+
#cfg(true)
264+
#cfg(false)
265+
#cfg(target="wasm")
266+
#cfg(not(target="wasm"))
267+
#cfg(all(target="wasm", true))
268+
#cfg(any(target="wasm", target="native"))
269+
```

0 commit comments

Comments
 (0)