rename expand to inline
This commit is contained in:
+3
-3
@@ -117,13 +117,13 @@ fields. `_` is not a keyword member name.
|
|||||||
- field access through struct values and pointers, index/slice bounds contextually coerced to `usize`, and unsigned narrower index support
|
- field access through struct values and pointers, index/slice bounds contextually coerced to `usize`, and unsigned narrower index support
|
||||||
- boolean `if` / `else if` / `else` and `for` loops with braceless single-statement bodies when the preceding expression is parenthesized or a function call
|
- boolean `if` / `else if` / `else` and `for` loops with braceless single-statement bodies when the preceding expression is parenthesized or a function call
|
||||||
- `while` loops with optional post-iteration update clauses
|
- `while` loops with optional post-iteration update clauses
|
||||||
- `for` loops over ranges, arrays, slices, and pointers-to-arrays with copy captures, pointer captures `|@item|`, and optional `usize` index captures; `expand for` specializes a comptime aggregate into one checked body per element
|
- `for` loops over ranges, arrays, slices, and pointers-to-arrays with copy captures, pointer captures `|@item|`, and optional `usize` index captures; `inline for` specializes a comptime aggregate into one checked body per element
|
||||||
- `break`, `continue`, labeled `break :label`, labeled `continue :label`, and labeled plain blocks; `break :label` can cross nested scopes to exit a labeled block
|
- `break`, `continue`, labeled `break :label`, labeled `continue :label`, and labeled plain blocks; `break :label` can cross nested scopes to exit a labeled block
|
||||||
- bare block scopes, `defer`, and fallible-function `errdefer` with optional error capture; cleanup is block-scoped and LIFO
|
- bare block scopes, `defer`, and fallible-function `errdefer` with optional error capture; cleanup is block-scoped and LIFO
|
||||||
- always-trapping `unreachable`, a `noreturn` expression that diagnoses use during comptime evaluation and terminates the current runtime path
|
- always-trapping `unreachable`, a `noreturn` expression that diagnoses use during comptime evaluation and terminates the current runtime path
|
||||||
- bare void `return`, same-line `return value`, value blocks, value `if` with implicit single-expression branches, value loops, value `match`, and strictly value-producing `yield value` / `yield :label value`
|
- bare void `return`, same-line `return value`, value blocks, value `if` with implicit single-expression branches, value loops, value `match`, and strictly value-producing `yield value` / `yield :label value`
|
||||||
- `match` statements/expressions over enums, tagged unions, and scalars, including exhaustiveness checks, payload captures, pointer payload captures, multi-pattern arms, and scalar range patterns
|
- `match` statements/expressions over enums, tagged unions, and scalars, including exhaustiveness checks, payload captures, pointer payload captures, multi-pattern arms, and scalar range patterns
|
||||||
- a final `expand |value|:` enum arm or `expand |payload[, tag]|:` tagged-union arm generates one specialized arm for each variant not covered earlier; enum values and optional tags are comptime-known, while union payloads keep their concrete variant type
|
- a final `inline |value|:` enum arm or `inline |payload[, tag]|:` tagged-union arm generates one specialized arm for each variant not covered earlier; enum values and optional tags are comptime-known, while union payloads keep their concrete variant type
|
||||||
- fallible `try` and uniform `catch [|e|] value_source` fallbacks; captures work with ordinary expressions, value blocks, and value-producing `if` / loops / `match`
|
- fallible `try` and uniform `catch [|e|] value_source` fallbacks; captures work with ordinary expressions, value blocks, and value-producing `if` / loops / `match`
|
||||||
- direct `return match ...` and `yield match ...` value-control-flow operands
|
- direct `return match ...` and `yield match ...` value-control-flow operands
|
||||||
|
|
||||||
@@ -238,7 +238,7 @@ exactly once. Bare functions named `memcopy` or `memset` remain ordinary user fu
|
|||||||
- `some!(value)` explicitly constructs the present branch of an expected optional, including nested optionals where `some!(null)` differs from outer `null`
|
- `some!(value)` explicitly constructs the present branch of an expected optional, including nested optionals where `some!(null)` differs from outer `null`
|
||||||
- tuple types are unnamed-field structs (`struct { i32, []u8 }`), tuple values use `{1, "bro"}` / `{1,}` / `{}`, and fields use canonical numeric names such as `.0`
|
- tuple types are unnamed-field structs (`struct { i32, []u8 }`), tuple values use `{1, "bro"}` / `{1,}` / `{}`, and fields use canonical numeric names such as `.0`
|
||||||
- anonymous keyed records use `{x = 1, name = "bro"}`; without context their declaration-ordered names and inferred value types form a structurally interned record type, while a record context applies that type's coercions and field defaults; `{}` remains an empty tuple without context and constructs an empty contextual record when a record is expected
|
- anonymous keyed records use `{x = 1, name = "bro"}`; without context their declaration-ordered names and inferred value types form a structurally interned record type, while a record context applies that type's coercions and field defaults; `{}` remains an empty tuple without context and constructs an empty contextual record when a record is expected
|
||||||
- `typeinfo!`, `field!`, `compile_error!`, and semantic `expand for` provide compile-time record and enum reflection and heterogeneous static expansion without runtime metadata; enum reflection exposes declaration-ordered fields, reflected aggregates remain persistent compile-time values, and expand-loop `break` / `continue` must be selected entirely at comptime
|
- `typeinfo!`, `field!`, `compile_error!`, and semantic `inline for` provide compile-time record and enum reflection and heterogeneous static expansion without runtime metadata; enum reflection exposes declaration-ordered fields, reflected aggregates remain persistent compile-time values, and inline-loop `break` / `continue` must be selected entirely at comptime
|
||||||
- `tag!(value)` reads a tagged union's active discriminant and folds when the value is comptime-known; `tagname!(enum_value)` requires a comptime-known enum value and returns its immutable declaration name
|
- `tag!(value)` reads a tagged union's active discriminant and folds when the value is comptime-known; `tagname!(enum_value)` requires a comptime-known enum value and returns its immutable declaration name
|
||||||
- bodyful `c_func` definitions and bodyless `c_func` declarations with exact external symbol names
|
- bodyful `c_func` definitions and bodyless `c_func` declarations with exact external symbol names
|
||||||
- concrete-only C signatures, C variadic declarations/calls, and C default argument promotions
|
- concrete-only C signatures, C variadic declarations/calls, and C default argument promotions
|
||||||
|
|||||||
@@ -856,8 +856,8 @@
|
|||||||
- tuples are unnamed-field structs with structural anonymous values, nominal named declarations,
|
- tuples are unnamed-field structs with structural anonymous values, nominal named declarations,
|
||||||
brace literals, numeric fields, and no runtime metadata
|
brace literals, numeric fields, and no runtime metadata
|
||||||
- `@std/meta`, `typeinfo!`, `field!`, `compile_error!`, specialization-time branches, and semantic
|
- `@std/meta`, `typeinfo!`, `field!`, `compile_error!`, specialization-time branches, and semantic
|
||||||
`expand for` use checker-owned persistent compile-time values for aggregate-first reflection and
|
`inline for` use checker-owned persistent compile-time values for aggregate-first reflection and
|
||||||
heterogeneous static expansion; expand-loop control is recursively resolved at comptime
|
heterogeneous static expansion; inline-loop control is recursively resolved at comptime
|
||||||
- interleaved comptime parameters use semantic candidate resolution, immutable byte values specialize
|
- interleaved comptime parameters use semantic candidate resolution, immutable byte values specialize
|
||||||
by contents, and all comptime parameters remain erased from the runtime ABI
|
by contents, and all comptime parameters remain erased from the runtime ABI
|
||||||
- `io.print(writer, format, args)` validates and expands `{s}` / `{d}` formatting at comptime,
|
- `io.print(writer, format, args)` validates and expands `{s}` / `{d}` formatting at comptime,
|
||||||
@@ -890,10 +890,9 @@
|
|||||||
- integer output uses one base-aware 65-byte stack buffer; float output uses fixed-buffer
|
- integer output uses one base-aware 65-byte stack buffer; float output uses fixed-buffer
|
||||||
libc `snprintf` with 32-bit and 64-bit general/scientific precision and propagates failure
|
libc `snprintf` with 32-bit and 64-bit general/scientific precision and propagates failure
|
||||||
|
|
||||||
40. compile-time `expand` (implemented)
|
40. compile-time `inline` (implemented)
|
||||||
- expansion-oriented `inline for` is strictly renamed to `expand for`; `inline` remains available
|
- expansion-oriented `expand for` is strictly renamed to `inline for`; `expand` is no longer a keyword
|
||||||
for future function-inlining syntax
|
- final inlined enum and tagged-union match arms generate checker-local specialized arms only
|
||||||
- final expanded enum and tagged-union match arms generate checker-local specialized arms only
|
|
||||||
for variants not covered by preceding explicit arms
|
for variants not covered by preceding explicit arms
|
||||||
- generated enum values and union tags are static bindings, heterogeneous payloads retain their
|
- generated enum values and union tags are static bindings, heterogeneous payloads retain their
|
||||||
concrete types, and `void` payloads support value and pointer captures without runtime storage
|
concrete types, and `void` payloads support value and pointer captures without runtime storage
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ Stmt :: struct {
|
|||||||
// list (empty marks the `else` arm; more than one is a multi-pattern arm),
|
// list (empty marks the `else` arm; more than one is a multi-pattern arm),
|
||||||
// `captures` for the optional payload capture (0 or 1 name, tagged-union variants
|
// `captures` for the optional payload capture (0 or 1 name, tagged-union variants
|
||||||
// only) with `pointer_capture` distinguishing `|@cap|` from `|cap|`, and `body`
|
// only) with `pointer_capture` distinguishing `|@cap|` from `|cap|`, and `body`
|
||||||
// as the arm body. An expanded arm has `expand` set, no patterns, and one or two
|
// as the arm body. An `inline` arm has `expand` set, no patterns, and one or two
|
||||||
// captures for its specialized value/payload and optional tagged-union tag.
|
// captures for its specialized value/payload and optional tagged-union tag.
|
||||||
captures: []symbol.Id,
|
captures: []symbol.Id,
|
||||||
// `Match_Arm` pattern list; empty ⇒ the `else` arm.
|
// `Match_Arm` pattern list; empty ⇒ the `else` arm.
|
||||||
|
|||||||
@@ -10574,7 +10574,7 @@ expand_field_bindings :: proc(
|
|||||||
value := state.values[value_id]
|
value := state.values[value_id]
|
||||||
if ct_value_contains_undefined(&state, value_id) {
|
if ct_value_contains_undefined(&state, value_id) {
|
||||||
if diagnose {
|
if diagnose {
|
||||||
_ = ct_fail(&state, .Not_Comptime, checker.ast_module.exprs[expr].span, "expand for cannot expand an undefined comptime value")
|
_ = ct_fail(&state, .Not_Comptime, checker.ast_module.exprs[expr].span, "inline for cannot iterate an undefined comptime value")
|
||||||
}
|
}
|
||||||
return nil, .Diagnosed if state.diagnostic != source.INVALID_DIAGNOSTIC else .Invalid
|
return nil, .Diagnosed if state.diagnostic != source.INVALID_DIAGNOSTIC else .Invalid
|
||||||
}
|
}
|
||||||
@@ -10712,7 +10712,7 @@ contains_expand_control :: proc(
|
|||||||
}
|
}
|
||||||
case .For, .While:
|
case .For, .While:
|
||||||
// Unlabelled control belongs to the nested loop. A labelled jump can still
|
// Unlabelled control belongs to the nested loop. A labelled jump can still
|
||||||
// name the surrounding expand loop and is therefore relevant here.
|
// name the surrounding inline loop and is therefore relevant here.
|
||||||
if contains_expand_control(checker, statement.body, target_label, false) {
|
if contains_expand_control(checker, statement.body, target_label, false) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -10817,7 +10817,7 @@ flatten_expand_iteration :: proc(
|
|||||||
if diagnostic != nil {
|
if diagnostic != nil {
|
||||||
diagnostic^ = source.add(
|
diagnostic^ = source.add(
|
||||||
checker.diagnostics, statement.span,
|
checker.diagnostics, statement.span,
|
||||||
"break or continue targeting an expand loop must be compile-time-resolvable",
|
"break or continue targeting an inline loop must be compile-time-resolvable",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return .Invalid
|
return .Invalid
|
||||||
@@ -10856,7 +10856,7 @@ flatten_expand_iteration :: proc(
|
|||||||
if diagnostic != nil {
|
if diagnostic != nil {
|
||||||
diagnostic^ = source.add(
|
diagnostic^ = source.add(
|
||||||
checker.diagnostics, statement.span,
|
checker.diagnostics, statement.span,
|
||||||
"break or continue targeting an expand loop must be compile-time-resolvable",
|
"break or continue targeting an inline loop must be compile-time-resolvable",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return .Invalid
|
return .Invalid
|
||||||
@@ -10882,7 +10882,7 @@ flatten_expand_iteration :: proc(
|
|||||||
if diagnostic != nil {
|
if diagnostic != nil {
|
||||||
diagnostic^ = source.add(
|
diagnostic^ = source.add(
|
||||||
checker.diagnostics, statement.span,
|
checker.diagnostics, statement.span,
|
||||||
"break or continue targeting an expand loop must be compile-time-resolvable",
|
"break or continue targeting an inline loop must be compile-time-resolvable",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return .Invalid
|
return .Invalid
|
||||||
@@ -11814,7 +11814,7 @@ build_block :: proc(
|
|||||||
case .For:
|
case .For:
|
||||||
if statement.expand {
|
if statement.expand {
|
||||||
if statement.pointer_capture {
|
if statement.pointer_capture {
|
||||||
id := source.add(checker.diagnostics, statement.span, "expand for does not support pointer captures")
|
id := source.add(checker.diagnostics, statement.span, "inline for does not support pointer captures")
|
||||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||||
append(&checker.module.statements, hir.Stmt{kind=.Trap, span=statement.span, diagnostic=id})
|
append(&checker.module.statements, hir.Stmt{kind=.Trap, span=statement.span, diagnostic=id})
|
||||||
ctx.problematic^ = true
|
ctx.problematic^ = true
|
||||||
@@ -11822,9 +11822,9 @@ build_block :: proc(
|
|||||||
}
|
}
|
||||||
bindings, expand_error := expand_field_bindings(checker, statement.expr, statement.name, ctx.pkg, ctx.file, true)
|
bindings, expand_error := expand_field_bindings(checker, statement.expr, statement.name, ctx.pkg, ctx.file, true)
|
||||||
if expand_error != .None && expand_error != .Diagnosed {
|
if expand_error != .None && expand_error != .Diagnosed {
|
||||||
message := "expand for requires a comptime tuple, fixed array, range, slice, or reflection value"
|
message := "inline for requires a comptime tuple, fixed array, range, slice, or reflection value"
|
||||||
if expand_error == .Quota {
|
if expand_error == .Quota {
|
||||||
message = "expand for expansion exceeds the compile-time evaluation quota"
|
message = "inline for expansion exceeds the compile-time evaluation quota"
|
||||||
}
|
}
|
||||||
id := source.add(
|
id := source.add(
|
||||||
checker.diagnostics, statement.span,
|
checker.diagnostics, statement.span,
|
||||||
@@ -12869,28 +12869,28 @@ emit_match :: proc(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if has_else || has_expand {
|
if has_else || has_expand {
|
||||||
message := "arms after 'else' are unreachable" if has_else else "arms after 'expand' are unreachable"
|
message := "arms after 'else' are unreachable" if has_else else "arms after 'inline' are unreachable"
|
||||||
source.add(checker.diagnostics, arm.span, message)
|
source.add(checker.diagnostics, arm.span, message)
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
if arm.expand {
|
if arm.expand {
|
||||||
if !is_tagged && !is_enum_subject {
|
if !is_tagged && !is_enum_subject {
|
||||||
source.add(checker.diagnostics, arm.span, "'expand' requires an enum or tagged-union match subject")
|
source.add(checker.diagnostics, arm.span, "'inline' requires an enum or tagged-union match subject")
|
||||||
ok = false
|
ok = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
expected_captures := 1 if is_enum_subject else 2
|
expected_captures := 1 if is_enum_subject else 2
|
||||||
if len(arm.captures) == 0 || len(arm.captures) > expected_captures {
|
if len(arm.captures) == 0 || len(arm.captures) > expected_captures {
|
||||||
description := "exactly one capture" if is_enum_subject else "one or two captures"
|
description := "exactly one capture" if is_enum_subject else "one or two captures"
|
||||||
source.addf(checker.diagnostics, arm.span, "expanded match on '%s' requires %s", type_label(checker, subject_type), description)
|
source.addf(checker.diagnostics, arm.span, "inlined match on '%s' requires %s", type_label(checker, subject_type), description)
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
if is_enum_subject && arm.pointer_capture {
|
if is_enum_subject && arm.pointer_capture {
|
||||||
source.add(checker.diagnostics, arm.span, "enum expansion does not support pointer captures")
|
source.add(checker.diagnostics, arm.span, "enum inlining does not support pointer captures")
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
if len(arm.captures) > 1 && arm.captures[0] != checker.sink_symbol && arm.captures[0] == arm.captures[1] {
|
if len(arm.captures) > 1 && arm.captures[0] != checker.sink_symbol && arm.captures[0] == arm.captures[1] {
|
||||||
source.add(checker.diagnostics, arm.span, "expand captures must have distinct names")
|
source.add(checker.diagnostics, arm.span, "inline captures must have distinct names")
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
remaining := 0
|
remaining := 0
|
||||||
@@ -12963,7 +12963,7 @@ emit_match :: proc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if remaining == 0 {
|
if remaining == 0 {
|
||||||
source.add(checker.diagnostics, arm.span, "redundant 'expand': the 'match' already covers every variant")
|
source.add(checker.diagnostics, arm.span, "redundant 'inline': the 'match' already covers every variant")
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
has_expand = true
|
has_expand = true
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ keyword_kind :: proc(text: string) -> token.Kind {
|
|||||||
case "if": return .Keyword_If
|
case "if": return .Keyword_If
|
||||||
case "while": return .Keyword_While
|
case "while": return .Keyword_While
|
||||||
case "for": return .Keyword_For
|
case "for": return .Keyword_For
|
||||||
case "expand": return .Keyword_Expand
|
case "inline": return .Keyword_Inline
|
||||||
case "break": return .Keyword_Break
|
case "break": return .Keyword_Break
|
||||||
case "continue": return .Keyword_Continue
|
case "continue": return .Keyword_Continue
|
||||||
case "defer": return .Keyword_Defer
|
case "defer": return .Keyword_Defer
|
||||||
|
|||||||
@@ -1813,16 +1813,7 @@ parse_value_control_flow :: proc(parser: ^Parser) -> (ast.Stmt_Id, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||||
if current(parser).kind == .Identifier && token_text(parser, current(parser)) == "inline" &&
|
if current(parser).kind == .Keyword_Inline && peek(parser).kind == .Keyword_For {
|
||||||
peek(parser).kind == .Keyword_For {
|
|
||||||
start := advance(parser)
|
|
||||||
id := parse_for(parser)
|
|
||||||
parser.module.statements[id].expand = true
|
|
||||||
parser.module.statements[id].span = span_from(start.span, parser.module.statements[id].span)
|
|
||||||
source.add(parser.diagnostics, start.span, "'inline for' was renamed to 'expand for'")
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
if current(parser).kind == .Keyword_Expand && peek(parser).kind == .Keyword_For {
|
|
||||||
start := advance(parser)
|
start := advance(parser)
|
||||||
id := parse_for(parser)
|
id := parse_for(parser)
|
||||||
parser.module.statements[id].expand = true
|
parser.module.statements[id].expand = true
|
||||||
@@ -2266,7 +2257,7 @@ parse_arm_body :: proc(parser: ^Parser) -> []ast.Stmt_Id {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse_match_arm parses one `<pattern,...> [|[@]capture|]: <body>`, `else: <body>`,
|
// parse_match_arm parses one `<pattern,...> [|[@]capture|]: <body>`, `else: <body>`,
|
||||||
// or `expand |[@]value[, tag]|: <body>` arm.
|
// or `inline |[@]value[, tag]|: <body>` arm.
|
||||||
parse_match_arm :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
parse_match_arm :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||||
start := current(parser).span
|
start := current(parser).span
|
||||||
patterns: [dynamic]ast.Expr_Id
|
patterns: [dynamic]ast.Expr_Id
|
||||||
@@ -2275,10 +2266,10 @@ parse_match_arm :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
captures.allocator = parser.module.allocator
|
captures.allocator = parser.module.allocator
|
||||||
pointer_capture := false
|
pointer_capture := false
|
||||||
expand := false
|
expand := false
|
||||||
if _, is_expand := allow(parser, .Keyword_Expand); is_expand {
|
if _, is_expand := allow(parser, .Keyword_Inline); is_expand {
|
||||||
expand = true
|
expand = true
|
||||||
if _, ok := allow(parser, .Pipe); !ok {
|
if _, ok := allow(parser, .Pipe); !ok {
|
||||||
source.add(parser.diagnostics, current(parser).span, "expected '|' before expand captures")
|
source.add(parser.diagnostics, current(parser).span, "expected '|' before inline captures")
|
||||||
} else {
|
} else {
|
||||||
if _, at_ok := allow(parser, .At); at_ok {
|
if _, at_ok := allow(parser, .At); at_ok {
|
||||||
pointer_capture = true
|
pointer_capture = true
|
||||||
@@ -2288,7 +2279,7 @@ parse_match_arm :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
advance(parser)
|
advance(parser)
|
||||||
append(&captures, name_tok.symbol)
|
append(&captures, name_tok.symbol)
|
||||||
} else {
|
} else {
|
||||||
source.add(parser.diagnostics, current(parser).span, "expected an expand value capture")
|
source.add(parser.diagnostics, current(parser).span, "expected an inline value capture")
|
||||||
}
|
}
|
||||||
if _, comma_ok := allow(parser, .Comma); comma_ok {
|
if _, comma_ok := allow(parser, .Comma); comma_ok {
|
||||||
tag_tok := current(parser)
|
tag_tok := current(parser)
|
||||||
@@ -2296,10 +2287,10 @@ parse_match_arm :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
advance(parser)
|
advance(parser)
|
||||||
append(&captures, tag_tok.symbol)
|
append(&captures, tag_tok.symbol)
|
||||||
} else {
|
} else {
|
||||||
source.add(parser.diagnostics, current(parser).span, "expected an expand tag capture")
|
source.add(parser.diagnostics, current(parser).span, "expected an inline tag capture")
|
||||||
}
|
}
|
||||||
if _, extra := allow(parser, .Comma); extra {
|
if _, extra := allow(parser, .Comma); extra {
|
||||||
source.add(parser.diagnostics, current(parser).span, "'expand' accepts at most two captures")
|
source.add(parser.diagnostics, current(parser).span, "'inline' accepts at most two captures")
|
||||||
for current(parser).kind != .Pipe && current(parser).kind != .Colon &&
|
for current(parser).kind != .Pipe && current(parser).kind != .Colon &&
|
||||||
current(parser).kind != .Newline && current(parser).kind != .Eof {
|
current(parser).kind != .Newline && current(parser).kind != .Eof {
|
||||||
advance(parser)
|
advance(parser)
|
||||||
@@ -2307,7 +2298,7 @@ parse_match_arm :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, close_ok := allow(parser, .Pipe); !close_ok {
|
if _, close_ok := allow(parser, .Pipe); !close_ok {
|
||||||
source.add(parser.diagnostics, current(parser).span, "expected '|' to close expand captures")
|
source.add(parser.diagnostics, current(parser).span, "expected '|' to close inline captures")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if _, is_else := allow(parser, .Keyword_Else); !is_else {
|
} else if _, is_else := allow(parser, .Keyword_Else); !is_else {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ Kind :: enum u8 {
|
|||||||
Keyword_If,
|
Keyword_If,
|
||||||
Keyword_While,
|
Keyword_While,
|
||||||
Keyword_For,
|
Keyword_For,
|
||||||
Keyword_Expand,
|
Keyword_Inline,
|
||||||
Keyword_Break,
|
Keyword_Break,
|
||||||
Keyword_Continue,
|
Keyword_Continue,
|
||||||
Keyword_Defer,
|
Keyword_Defer,
|
||||||
|
|||||||
+79
-28
@@ -177,6 +177,21 @@ lexer_preserves_newlines_and_skips_comments :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, stream.items[1].kind, token.Kind.Identifier)
|
testing.expect_value(t, stream.items[1].kind, token.Kind.Identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
inline_is_keyword_and_expand_is_identifier :: proc(t: ^testing.T) {
|
||||||
|
source_file := source.Source{path="test.bro", text="inline expand"}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||||
|
defer delete(stream.items)
|
||||||
|
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
|
testing.expect_value(t, stream.items[0].kind, token.Kind.Keyword_Inline)
|
||||||
|
testing.expect_value(t, stream.items[1].kind, token.Kind.Identifier)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
lexer_recognizes_bitwise_operators_with_longest_match :: proc(t: ^testing.T) {
|
lexer_recognizes_bitwise_operators_with_longest_match :: proc(t: ^testing.T) {
|
||||||
source_file := source.Source{path="test.bro", text="~ & &= | |= xor xor= << <<= >> >>= <<| <<|= ^"}
|
source_file := source.Source{path="test.bro", text="~ & &= | |= xor xor= << <<= >> >>= <<| <<|= ^"}
|
||||||
@@ -1253,6 +1268,45 @@ main func() void {
|
|||||||
testing.expect(t, !found_old_function_error)
|
testing.expect(t, !found_old_function_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
function_signature_context_infers_enum_literals :: proc(t: ^testing.T) {
|
||||||
|
text := `TokenKind :: enum {
|
||||||
|
ident
|
||||||
|
double_colon
|
||||||
|
newline
|
||||||
|
}
|
||||||
|
ParseError :: enum {
|
||||||
|
unexpected_token
|
||||||
|
}
|
||||||
|
parse func() void ! ParseError {
|
||||||
|
try expect(.ident)
|
||||||
|
try expect(.double_colon)
|
||||||
|
try expect(.newline)
|
||||||
|
}
|
||||||
|
expect func(kind TokenKind) void ! ParseError {
|
||||||
|
if kind == .newline {
|
||||||
|
return .unexpected_token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main func() void {
|
||||||
|
parse() catch |_| {}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||||
|
defer delete(stream.items)
|
||||||
|
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&ast_module)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
pipeline_emits_specialized_calling_conventions_and_checked_add :: proc(t: ^testing.T) {
|
pipeline_emits_specialized_calling_conventions_and_checked_add :: proc(t: ^testing.T) {
|
||||||
text := `sum_c c_func(a, b int) int {
|
text := `sum_c c_func(a, b int) int {
|
||||||
@@ -3236,10 +3290,10 @@ milestone_37_tuples_reflection_expand_for_and_debug_print_compile_and_run :: pro
|
|||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
expanded_matches_and_tag_intrinsics_compile_and_run :: proc(t: ^testing.T) {
|
expanded_matches_and_tag_intrinsics_compile_and_run :: proc(t: ^testing.T) {
|
||||||
output := "/tmp/brolang-test-expand"
|
output := "/tmp/brolang-test-inline"
|
||||||
defer _ = os.remove(output)
|
defer _ = os.remove(output)
|
||||||
status := compiler_core.compile_package(
|
status := compiler_core.compile_package(
|
||||||
"examples/programs/expand", output, nil, target.DEFAULT, cimport.Options{}, ".",
|
"examples/programs/inline", output, nil, target.DEFAULT, cimport.Options{}, ".",
|
||||||
)
|
)
|
||||||
testing.expect_value(t, status, 0)
|
testing.expect_value(t, status, 0)
|
||||||
state, stdout, stderr, _ := os2.process_exec(
|
state, stdout, stderr, _ := os2.process_exec(
|
||||||
@@ -3261,29 +3315,29 @@ main func() void {
|
|||||||
inline for {1} |value| { _ = value }
|
inline for {1} |value| { _ = value }
|
||||||
|
|
||||||
n i32 = 1
|
n i32 = 1
|
||||||
match n { expand |value|: _ = value }
|
match n { inline |value|: _ = value }
|
||||||
|
|
||||||
e E = .a
|
e E = .a
|
||||||
match e { expand |value, tag|: _ = value }
|
match e { inline |value, tag|: _ = value }
|
||||||
match e {
|
match e {
|
||||||
.a, .b: {}
|
.a, .b: {}
|
||||||
expand |value|: _ = value
|
inline |value|: _ = value
|
||||||
}
|
}
|
||||||
match e {
|
match e {
|
||||||
expand |value|: _ = value
|
inline |value|: _ = value
|
||||||
.b: {}
|
.b: {}
|
||||||
}
|
}
|
||||||
match e {
|
match e {
|
||||||
else: {}
|
else: {}
|
||||||
expand |value|: _ = value
|
inline |value|: _ = value
|
||||||
}
|
}
|
||||||
|
|
||||||
u Plain = Plain{value = 1}
|
u Plain = Plain{value = 1}
|
||||||
_ = tag!(u)
|
_ = tag!(u)
|
||||||
_ = tagname!(e)
|
_ = tagname!(e)
|
||||||
|
|
||||||
match e { expand ||: {} }
|
match e { inline ||: {} }
|
||||||
match e { expand |a, b, c|: {} }
|
match e { inline |a, b, c|: {} }
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
source_file := source.Source{path="expand_diagnostics.bro", text=text}
|
source_file := source.Source{path="expand_diagnostics.bro", text=text}
|
||||||
@@ -3298,7 +3352,6 @@ main func() void {
|
|||||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
defer hir.destroy_module(&hir_module)
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
found_old := false
|
|
||||||
found_subject := false
|
found_subject := false
|
||||||
found_captures := false
|
found_captures := false
|
||||||
found_redundant := false
|
found_redundant := false
|
||||||
@@ -3309,17 +3362,15 @@ main func() void {
|
|||||||
found_tagname := false
|
found_tagname := false
|
||||||
for diagnostic in diagnostics.items {
|
for diagnostic in diagnostics.items {
|
||||||
message := diagnostic.message
|
message := diagnostic.message
|
||||||
found_old = found_old || strings.contains(message, "'inline for' was renamed to 'expand for'")
|
found_subject = found_subject || strings.contains(message, "'inline' requires an enum or tagged-union")
|
||||||
found_subject = found_subject || strings.contains(message, "'expand' requires an enum or tagged-union")
|
|
||||||
found_captures = found_captures || strings.contains(message, "requires exactly one capture")
|
found_captures = found_captures || strings.contains(message, "requires exactly one capture")
|
||||||
found_redundant = found_redundant || strings.contains(message, "redundant 'expand'")
|
found_redundant = found_redundant || strings.contains(message, "redundant 'inline'")
|
||||||
found_after = found_after || strings.contains(message, "arms after 'expand' are unreachable") || strings.contains(message, "arms after 'else' are unreachable")
|
found_after = found_after || strings.contains(message, "arms after 'inline' are unreachable") || strings.contains(message, "arms after 'else' are unreachable")
|
||||||
found_missing = found_missing || strings.contains(message, "expected an expand value capture")
|
found_missing = found_missing || strings.contains(message, "expected an inline value capture")
|
||||||
found_many = found_many || strings.contains(message, "at most two captures")
|
found_many = found_many || strings.contains(message, "at most two captures")
|
||||||
found_tag = found_tag || strings.contains(message, "tag! requires a tagged-union value")
|
found_tag = found_tag || strings.contains(message, "tag! requires a tagged-union value")
|
||||||
found_tagname = found_tagname || strings.contains(message, "tagname! requires a comptime-known enum value")
|
found_tagname = found_tagname || strings.contains(message, "tagname! requires a comptime-known enum value")
|
||||||
}
|
}
|
||||||
testing.expect(t, found_old)
|
|
||||||
testing.expect(t, found_subject)
|
testing.expect(t, found_subject)
|
||||||
testing.expect(t, found_captures)
|
testing.expect(t, found_captures)
|
||||||
testing.expect(t, found_redundant)
|
testing.expect(t, found_redundant)
|
||||||
@@ -3751,7 +3802,7 @@ main func() i32 { return answer }
|
|||||||
milestone_37_expand_loop_control_must_be_statically_resolvable :: proc(t: ^testing.T) {
|
milestone_37_expand_loop_control_must_be_statically_resolvable :: proc(t: ^testing.T) {
|
||||||
text := `main func() void {
|
text := `main func() void {
|
||||||
total i32 = 0
|
total i32 = 0
|
||||||
expand for {1, 2} |value| {
|
inline for {1, 2} |value| {
|
||||||
if total == 0 {
|
if total == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -3775,7 +3826,7 @@ milestone_37_expand_loop_control_must_be_statically_resolvable :: proc(t: ^testi
|
|||||||
for diagnostic in diagnostics.items {
|
for diagnostic in diagnostics.items {
|
||||||
found = found || strings.contains(
|
found = found || strings.contains(
|
||||||
diagnostic.message,
|
diagnostic.message,
|
||||||
"break or continue targeting an expand loop must be compile-time-resolvable",
|
"break or continue targeting an inline loop must be compile-time-resolvable",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
testing.expect(t, found)
|
testing.expect(t, found)
|
||||||
@@ -3803,7 +3854,7 @@ read_initialized_sibling func() i32 {
|
|||||||
answer :: $read_initialized_sibling()
|
answer :: $read_initialized_sibling()
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
total usize = 0
|
total usize = 0
|
||||||
expand for make_tokens() |token| {
|
inline for make_tokens() |token| {
|
||||||
total += token.text.len + token.count
|
total += token.text.len + token.count
|
||||||
}
|
}
|
||||||
if answer != 42 or total != 8 {
|
if answer != 42 or total != 8 {
|
||||||
@@ -3884,8 +3935,8 @@ milestone_37_expand_expansions_keep_distinct_call_resolutions :: proc(t: ^testin
|
|||||||
}
|
}
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
total i64 = 0
|
total i64 = 0
|
||||||
expand for {{i8(1), i16(2)}, {i32(3), i64(4)}} |row| {
|
inline for {{i8(1), i16(2)}, {i32(3), i64(4)}} |row| {
|
||||||
expand for row |value| {
|
inline for row |value| {
|
||||||
total += i64(identity(value))
|
total += i64(identity(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3917,13 +3968,13 @@ main func() i32 {
|
|||||||
milestone_37_expand_control_prunes_inference_after_static_exit :: proc(t: ^testing.T) {
|
milestone_37_expand_control_prunes_inference_after_static_exit :: proc(t: ^testing.T) {
|
||||||
text := `take_i8 func(value i8) void { _ = value }
|
text := `take_i8 func(value i8) void { _ = value }
|
||||||
main func() void {
|
main func() void {
|
||||||
expand for {i8(1), "skip"} |value, index| {
|
inline for {i8(1), "skip"} |value, index| {
|
||||||
if index == 1 {
|
if index == 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
take_i8(value)
|
take_i8(value)
|
||||||
}
|
}
|
||||||
expand for {i8(1), "stop"} |value, index| {
|
inline for {i8(1), "stop"} |value, index| {
|
||||||
if index == 1 {
|
if index == 1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -3954,7 +4005,7 @@ StringToken :: struct { kind Kind, value []u8 }
|
|||||||
StopToken :: struct { kind Kind, value bool }
|
StopToken :: struct { kind Kind, value bool }
|
||||||
take_i8 func(value i8) void { _ = value }
|
take_i8 func(value i8) void { _ = value }
|
||||||
main func() void {
|
main func() void {
|
||||||
expand for {
|
inline for {
|
||||||
IntToken {kind = .integer, value = 1},
|
IntToken {kind = .integer, value = 1},
|
||||||
StringToken {kind = .string, value = "ok"},
|
StringToken {kind = .string, value = "ok"},
|
||||||
StopToken {kind = .stop, value = false},
|
StopToken {kind = .stop, value = false},
|
||||||
@@ -3968,7 +4019,7 @@ main func() void {
|
|||||||
.stop: break
|
.stop: break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expand for {i8(2), "skip", "stop"} |value, index| {
|
inline for {i8(2), "skip", "stop"} |value, index| {
|
||||||
match index {
|
match index {
|
||||||
0: {}
|
0: {}
|
||||||
1..=1, 7: continue
|
1..=1, 7: continue
|
||||||
@@ -6742,7 +6793,7 @@ via_nested func() i32 ! AError {
|
|||||||
via_expand func() i32 ! AError {
|
via_expand func() i32 ! AError {
|
||||||
abc(1) catch |err| {
|
abc(1) catch |err| {
|
||||||
match err {
|
match err {
|
||||||
expand |tag|: match tag {
|
inline |tag|: match tag {
|
||||||
.a: return err
|
.a: return err
|
||||||
else: unreachable
|
else: unreachable
|
||||||
}
|
}
|
||||||
@@ -15247,7 +15298,7 @@ Map func($E, $V type) type {
|
|||||||
init func($E, $V type, values meta.EnumFieldStruct(E, ?V, some!(null))) Map(E, V) {
|
init func($E, $V type, values meta.EnumFieldStruct(E, ?V, some!(null))) Map(E, V) {
|
||||||
map Map(E, V) = undefined
|
map Map(E, V) = undefined
|
||||||
match typeinfo!(E) {
|
match typeinfo!(E) {
|
||||||
.enum |info|: expand for info.fields |field, index| {
|
.enum |info|: inline for info.fields |field, index| {
|
||||||
map.present[index] = false
|
map.present[index] = false
|
||||||
if field!(values, field.name) |value| {
|
if field!(values, field.name) |value| {
|
||||||
map.present[index] = true
|
map.present[index] = true
|
||||||
@@ -15261,7 +15312,7 @@ init func($E, $V type, values meta.EnumFieldStruct(E, ?V, some!(null))) Map(E, V
|
|||||||
|
|
||||||
get func($E, $V type, map @Map(E, V), key E) ?V {
|
get func($E, $V type, map @Map(E, V), key E) ?V {
|
||||||
match typeinfo!(E) {
|
match typeinfo!(E) {
|
||||||
.enum |info|: expand for info.fields |field, index| {
|
.enum |info|: inline for info.fields |field, index| {
|
||||||
if key == field!(E, field.name) {
|
if key == field!(E, field.name) {
|
||||||
if map.present[index] { return map.values[index] }
|
if map.present[index] { return map.values[index] }
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Value :: union(enum) {
|
|||||||
enum_score func(kind Kind) i32 {
|
enum_score func(kind Kind) i32 {
|
||||||
result :: match kind {
|
result :: match kind {
|
||||||
.first: 1
|
.first: 1
|
||||||
expand |value|: {
|
inline |value|: {
|
||||||
yield match value {
|
yield match value {
|
||||||
.second: 2
|
.second: 2
|
||||||
.third: 3
|
.third: 3
|
||||||
@@ -31,7 +31,7 @@ enum_score func(kind Kind) i32 {
|
|||||||
equal_value func(a, b Value) bool {
|
equal_value func(a, b Value) bool {
|
||||||
if (tag!(a) != tag!(b)) return false
|
if (tag!(a) != tag!(b)) return false
|
||||||
result :: match a {
|
result :: match a {
|
||||||
expand |value, tag|: {
|
inline |value, tag|: {
|
||||||
yield match tag {
|
yield match tag {
|
||||||
.number: value == field!(b, tagname!(tag))
|
.number: value == field!(b, tagname!(tag))
|
||||||
.pair: {
|
.pair: {
|
||||||
@@ -49,7 +49,7 @@ equal_value func(a, b Value) bool {
|
|||||||
|
|
||||||
increment func(value @mut Value) void {
|
increment func(value @mut Value) void {
|
||||||
match value^ {
|
match value^ {
|
||||||
expand |@payload, tag|: match tag {
|
inline |@payload, tag|: match tag {
|
||||||
.number: payload^ += 1
|
.number: payload^ += 1
|
||||||
.pair: payload.left += 1
|
.pair: payload.left += 1
|
||||||
.empty: _ = payload
|
.empty: _ = payload
|
||||||
@@ -11,7 +11,7 @@ format func() []u8 {
|
|||||||
sum func($T type, value T) i32 {
|
sum func($T type, value T) i32 {
|
||||||
total i32 = 0
|
total i32 = 0
|
||||||
match typeinfo!(T) {
|
match typeinfo!(T) {
|
||||||
.record |record|: expand for record.fields |field| {
|
.record |record|: inline for record.fields |field| {
|
||||||
total += i32(field!(value, field.name))
|
total += i32(field!(value, field.name))
|
||||||
}
|
}
|
||||||
else: compile_error!("sum requires a record")
|
else: compile_error!("sum requires a record")
|
||||||
@@ -22,7 +22,7 @@ sum func($T type, value T) i32 {
|
|||||||
static_control func($T type, value T) i32 {
|
static_control func($T type, value T) i32 {
|
||||||
total i32 = 0
|
total i32 = 0
|
||||||
match typeinfo!(T) {
|
match typeinfo!(T) {
|
||||||
.record |record|: expand for record.fields |field| {
|
.record |record|: inline for record.fields |field| {
|
||||||
{
|
{
|
||||||
if field.index == 1 {
|
if field.index == 1 {
|
||||||
continue
|
continue
|
||||||
@@ -50,7 +50,7 @@ row_value func(row Row) i32 {
|
|||||||
|
|
||||||
static_aggregates func() i32 {
|
static_aggregates func() i32 {
|
||||||
total i32 = 0
|
total i32 = 0
|
||||||
expand for {Row {value = 2}, Row {value = 40}} |row| {
|
inline for {Row {value = 2}, Row {value = 40}} |row| {
|
||||||
total += row_value(row)
|
total += row_value(row)
|
||||||
}
|
}
|
||||||
return total
|
return total
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@ init func(
|
|||||||
map EnumMap(E, V) = undefined
|
map EnumMap(E, V) = undefined
|
||||||
|
|
||||||
match typeinfo!(E) {
|
match typeinfo!(E) {
|
||||||
.enum |info|: expand for info.fields |field, i| {
|
.enum |info|: inline for info.fields |field, i| {
|
||||||
map.present[i] = false
|
map.present[i] = false
|
||||||
|
|
||||||
if field!(values, field.name) |value| {
|
if field!(values, field.name) |value| {
|
||||||
@@ -34,7 +34,7 @@ init func(
|
|||||||
get func($E, $V type, map @EnumMap(E, V), key E) ?V {
|
get func($E, $V type, map @EnumMap(E, V), key E) ?V {
|
||||||
# fixme: linear lookup; implement an enum index/discriminant map for O(1) lookup
|
# fixme: linear lookup; implement an enum index/discriminant map for O(1) lookup
|
||||||
match typeinfo!(E) {
|
match typeinfo!(E) {
|
||||||
.enum |info|: expand for info.fields |field, i| {
|
.enum |info|: inline for info.fields |field, i| {
|
||||||
if key == field!(E, field.name) {
|
if key == field!(E, field.name) {
|
||||||
if (map.present[i]) return map.values[i]
|
if (map.present[i]) return map.values[i]
|
||||||
return null
|
return null
|
||||||
|
|||||||
+2
-2
@@ -111,7 +111,7 @@ stderr func(io Io) Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print func(output Writer, $format []u8, $Args type, args Args) void ! WriteError {
|
print func(output Writer, $format []u8, $Args type, args Args) void ! WriteError {
|
||||||
expand for parse_format(format.len, format, Args) |token| {
|
inline for parse_format(format.len, format, Args) |token| {
|
||||||
match token.kind {
|
match token.kind {
|
||||||
.unused: break
|
.unused: break
|
||||||
.literal: try write_all(output, format[token.start..token.end])
|
.literal: try write_all(output, format[token.start..token.end])
|
||||||
@@ -427,7 +427,7 @@ hide write_default func(output Writer, $T type, value T) void ! WriteError {
|
|||||||
.pointer: try write_all(output, value)
|
.pointer: try write_all(output, value)
|
||||||
.slice: try write_all(output, value)
|
.slice: try write_all(output, value)
|
||||||
.enum |enum_info|: {
|
.enum |enum_info|: {
|
||||||
expand for enum_info.fields |field| {
|
inline for enum_info.fields |field| {
|
||||||
if value == field!(T, field.name) {
|
if value == field!(T, field.name) {
|
||||||
try write_all(output, ".")
|
try write_all(output, ".")
|
||||||
try write_all(output, field.name)
|
try write_all(output, field.name)
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ EnumFieldStruct func($E, $Field type, $default ?Field) type {
|
|||||||
names [info.fields.len]mut []u8 = undefined
|
names [info.fields.len]mut []u8 = undefined
|
||||||
field_types [info.fields.len]mut type = undefined
|
field_types [info.fields.len]mut type = undefined
|
||||||
defaults [info.fields.len]mut ?Field = undefined
|
defaults [info.fields.len]mut ?Field = undefined
|
||||||
expand for info.fields |field, index| {
|
inline for info.fields |field, index| {
|
||||||
names[index] = field.name
|
names[index] = field.name
|
||||||
field_types[index] = Field
|
field_types[index] = Field
|
||||||
defaults[index] = default
|
defaults[index] = default
|
||||||
|
|||||||
Reference in New Issue
Block a user