style: align layout and ownership documentation
This commit is contained in:
+2
-2
@@ -426,7 +426,7 @@ set_source proc(
|
|||||||
try arraylist.append(&builder.storage.sources,
|
try arraylist.append(&builder.storage.sources,
|
||||||
diagnostics.Source{
|
diagnostics.Source{
|
||||||
path = path,
|
path = path,
|
||||||
text = text
|
text = text,
|
||||||
})
|
})
|
||||||
builder.source = text
|
builder.source = text
|
||||||
builder.source_id = id
|
builder.source_id = id
|
||||||
@@ -446,7 +446,7 @@ remove_source proc(builder @mut Builder) void {
|
|||||||
builder.source = builder.storage.sources.items[usize(builder.source_id)].text
|
builder.source = builder.storage.sources.items[usize(builder.source_id)].text
|
||||||
}
|
}
|
||||||
|
|
||||||
#! borrows builder storage; refresh after builder mutation and never pass this view to deinit.
|
#! borrows `builder` storage; refresh after builder mutation and never pass this view to deinit.
|
||||||
view proc(builder @Builder, root NodeId) Tree {
|
view proc(builder @Builder, root NodeId) Tree {
|
||||||
return Tree{ storage = builder.storage, root = root }
|
return Tree{ storage = builder.storage, root = root }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ add_package proc(compilation @mut Compilation) PackageId ! mem.AllocError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#! copies `path`, scans/parses into the shared arena and takes `bytes` only on success.
|
#! copies `path`, scans/parses into the shared arena and takes `bytes` only on success.
|
||||||
#! failure leaves bytes caller-owned and rolls back source diagnostics; interned names may remain.
|
#! failure leaves `bytes` caller-owned and rolls back source diagnostics; interned names may remain.
|
||||||
add_source proc(
|
add_source proc(
|
||||||
compilation @mut Compilation,
|
compilation @mut Compilation,
|
||||||
package PackageId,
|
package PackageId,
|
||||||
@@ -329,6 +329,7 @@ package_root proc(compilation @mut Compilation, package PackageId) ast.NodeId !
|
|||||||
|
|
||||||
#! checks every package in a separate scope and lowers one module with root-only entry selection.
|
#! checks every package in a separate scope and lowers one module with root-only entry selection.
|
||||||
#! returned IR borrows no frontend data; release it with ir.deinit.
|
#! returned IR borrows no frontend data; release it with ir.deinit.
|
||||||
|
#! temporary compilation storage uses the retained allocator and is released before returning.
|
||||||
compile proc(
|
compile proc(
|
||||||
compilation @mut Compilation,
|
compilation @mut Compilation,
|
||||||
root_package PackageId,
|
root_package PackageId,
|
||||||
@@ -357,12 +358,16 @@ compile proc(
|
|||||||
name := strpool.no_str
|
name := strpool.no_str
|
||||||
failure ?diagnostics.DiagnosticId := null
|
failure ?diagnostics.DiagnosticId := null
|
||||||
for (diagnostic_store.list.items) |diagnostic, index| {
|
for (diagnostic_store.list.items) |diagnostic, index| {
|
||||||
if (diagnostic.span.source == imported.path.source
|
if (
|
||||||
|
diagnostic.span.source == imported.path.source
|
||||||
and diagnostic.span.start == imported.path.start
|
and diagnostic.span.start == imported.path.start
|
||||||
and diagnostic.span.end == imported.path.end
|
and diagnostic.span.end == imported.path.end
|
||||||
and (diagnostic.code == .package_unavailable
|
and (
|
||||||
|
diagnostic.code == .package_unavailable
|
||||||
or diagnostic.code == .invalid_import_path
|
or diagnostic.code == .invalid_import_path
|
||||||
or diagnostic.code == .import_cycle)) {
|
or diagnostic.code == .import_cycle
|
||||||
|
)
|
||||||
|
) {
|
||||||
failure = diagnostics.DiagnosticId(index)
|
failure = diagnostics.DiagnosticId(index)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -379,8 +384,12 @@ compile proc(
|
|||||||
candidate :: path[start..]
|
candidate :: path[start..]
|
||||||
valid := candidate.len > 0
|
valid := candidate.len > 0
|
||||||
for (candidate) |byte, index| {
|
for (candidate) |byte, index| {
|
||||||
if (!(byte >= 'a' and byte <= 'z') and !(byte >= 'A' and byte <= 'Z')
|
if (
|
||||||
and byte != '_' and !(index > 0 and byte >= '0' and byte <= '9')) valid = false
|
!(byte >= 'a' and byte <= 'z')
|
||||||
|
and !(byte >= 'A' and byte <= 'Z')
|
||||||
|
and byte != '_'
|
||||||
|
and !(index > 0 and byte >= '0' and byte <= '9')
|
||||||
|
) valid = false
|
||||||
}
|
}
|
||||||
if (valid) {
|
if (valid) {
|
||||||
name = try strpool.intern(&compilation.strings, candidate)
|
name = try strpool.intern(&compilation.strings, candidate)
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ run proc(
|
|||||||
|
|
||||||
#! executes an isolated compile-time module with bounded work and stack depth.
|
#! executes an isolated compile-time module with bounded work and stack depth.
|
||||||
#! ownership and diagnostic identities follow run.
|
#! ownership and diagnostic identities follow run.
|
||||||
|
#! temporary execution storage uses `allocator` and is released before returning.
|
||||||
run_evaluation proc(
|
run_evaluation proc(
|
||||||
allocator mem.Allocator,
|
allocator mem.Allocator,
|
||||||
module @ir.Module,
|
module @ir.Module,
|
||||||
|
|||||||
@@ -254,6 +254,7 @@ Module :: struct {
|
|||||||
entry_diagnostic diagnostics.DiagnosticId = diagnostics.no_diagnostic
|
entry_diagnostic diagnostics.DiagnosticId = diagnostics.no_diagnostic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#! retains `allocator` for executable storage; release the returned module with deinit.
|
||||||
init proc(allocator mem.Allocator) Module {
|
init proc(allocator mem.Allocator) Module {
|
||||||
return Module{
|
return Module{
|
||||||
allocator = allocator,
|
allocator = allocator,
|
||||||
@@ -264,6 +265,7 @@ init proc(allocator mem.Allocator) Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#! retains `allocator` in its lists; transfer the function to a module using the same allocator for cleanup with deinit.
|
||||||
init_function proc(allocator mem.Allocator) Function {
|
init_function proc(allocator mem.Allocator) Function {
|
||||||
return Function{
|
return Function{
|
||||||
parameters = arraylist.init(allocator),
|
parameters = arraylist.init(allocator),
|
||||||
@@ -273,6 +275,7 @@ init_function proc(allocator mem.Allocator) Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#! retains `allocator` in its lists; transfer the block to a module function for cleanup with deinit.
|
||||||
init_block proc(allocator mem.Allocator) Block {
|
init_block proc(allocator mem.Allocator) Block {
|
||||||
return Block{
|
return Block{
|
||||||
instructions = arraylist.init(allocator),
|
instructions = arraylist.init(allocator),
|
||||||
|
|||||||
@@ -91,8 +91,13 @@ emit proc(
|
|||||||
for (module.functions.items[..index]) |previous| {
|
for (module.functions.items[..index]) |previous| {
|
||||||
if (mem.eql(previous.external_name, function.external_name)) duplicate = true
|
if (mem.eql(previous.external_name, function.external_name)) duplicate = true
|
||||||
}
|
}
|
||||||
if (duplicate or mem.eql(function.external_name, "write") or mem.eql(function.external_name, "__error")
|
if (
|
||||||
or mem.eql(function.external_name, "snprintf") or mem.eql(function.external_name, "exit")) continue
|
duplicate
|
||||||
|
or mem.eql(function.external_name, "write")
|
||||||
|
or mem.eql(function.external_name, "__error")
|
||||||
|
or mem.eql(function.external_name, "snprintf")
|
||||||
|
or mem.eql(function.external_name, "exit")
|
||||||
|
) continue
|
||||||
}
|
}
|
||||||
values :: try mem.alloc(Type, allocator, function.value_count)
|
values :: try mem.alloc(Type, allocator, function.value_count)
|
||||||
defer mem.free(allocator, values)
|
defer mem.free(allocator, values)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Error :: alias (
|
|||||||
| io.ReadError
|
| io.ReadError
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#! returns caller-owned storage; release it with mem.free using the same allocator.
|
||||||
@hide:file
|
@hide:file
|
||||||
copy proc(allocator mem.Allocator, text []u8) []mut u8 ! mem.AllocError {
|
copy proc(allocator mem.Allocator, text []u8) []mut u8 ! mem.AllocError {
|
||||||
bytes :: try mem.alloc(u8, allocator, text.len)
|
bytes :: try mem.alloc(u8, allocator, text.len)
|
||||||
@@ -21,6 +22,7 @@ copy proc(allocator mem.Allocator, text []u8) []mut u8 ! mem.AllocError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#! returns an owned canonical package directory for either a file or directory input.
|
#! returns an owned canonical package directory for either a file or directory input.
|
||||||
|
#! free the returned slice with mem.free using the same allocator; temporary storage is released before returning.
|
||||||
directory proc(allocator mem.Allocator, path []u8) []mut u8 ! Error {
|
directory proc(allocator mem.Allocator, path []u8) []mut u8 ! Error {
|
||||||
resolved :: try fs.canonical(allocator, path)
|
resolved :: try fs.canonical(allocator, path)
|
||||||
errdefer mem.free(allocator, resolved)
|
errdefer mem.free(allocator, resolved)
|
||||||
@@ -34,11 +36,14 @@ directory proc(allocator mem.Allocator, path []u8) []mut u8 ! Error {
|
|||||||
return parent
|
return parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#! returns a caller-owned joined path; release it with mem.free using the same allocator.
|
||||||
@hide:file
|
@hide:file
|
||||||
join proc(allocator mem.Allocator, base, name []u8) []mut u8 ! mem.AllocError {
|
join proc(allocator mem.Allocator, base, name []u8) []mut u8 ! mem.AllocError {
|
||||||
separator :: if (base.len == 1 and base[0] == '/') usize(0) else usize(1)
|
separator :: if (base.len == 1 and base[0] == '/') usize(0) else usize(1)
|
||||||
if (base.len > maxval!(usize) - separator or name.len > maxval!(usize) - base.len - separator)
|
if (
|
||||||
return .out_of_memory
|
base.len > maxval!(usize) - separator
|
||||||
|
or name.len > maxval!(usize) - base.len - separator
|
||||||
|
) return .out_of_memory
|
||||||
|
|
||||||
bytes :: try mem.alloc(u8, allocator, base.len + separator + name.len)
|
bytes :: try mem.alloc(u8, allocator, base.len + separator + name.len)
|
||||||
memcopy!(bytes[..base.len], base)
|
memcopy!(bytes[..base.len], base)
|
||||||
@@ -55,12 +60,15 @@ exists proc(allocator mem.Allocator, base, name []u8) bool ! Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#! finds the nearest ancestor build.hon/build.bro, otherwise uses the supplied directory.
|
#! finds the nearest ancestor build.hon/build.bro, otherwise uses the supplied directory.
|
||||||
|
#! free the returned slice with mem.free using the same allocator; temporary search storage is released before returning.
|
||||||
project_root proc(allocator mem.Allocator, package_dir []u8) []mut u8 ! Error {
|
project_root proc(allocator mem.Allocator, package_dir []u8) []mut u8 ! Error {
|
||||||
end := package_dir.len
|
end := package_dir.len
|
||||||
while true {
|
while true {
|
||||||
base :: package_dir[..end]
|
base :: package_dir[..end]
|
||||||
if (try exists(allocator, base, "build.hon") or try exists(allocator, base, "build.bro"))
|
if (
|
||||||
return try copy(allocator, base)
|
try exists(allocator, base, "build.hon")
|
||||||
|
or try exists(allocator, base, "build.bro")
|
||||||
|
) return try copy(allocator, base)
|
||||||
|
|
||||||
if (end == 1) break
|
if (end == 1) break
|
||||||
while (end > 1 and package_dir[end - 1] != '/') end -= 1
|
while (end > 1 and package_dir[end - 1] != '/') end -= 1
|
||||||
@@ -70,9 +78,12 @@ project_root proc(allocator mem.Allocator, package_dir []u8) []mut u8 ! Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#! relative imports start at the importing package; @ imports start at the project root.
|
#! relative imports start at the importing package; @ imports start at the project root.
|
||||||
|
#! free the returned slice with mem.free using the same allocator; temporary path storage is released before returning.
|
||||||
resolve proc(allocator mem.Allocator, root_dir, package_dir, import_path []u8) []mut u8 ! Error {
|
resolve proc(allocator mem.Allocator, root_dir, package_dir, import_path []u8) []mut u8 ! Error {
|
||||||
if (import_path.len == 0 or import_path[0] == '/') return .invalid_path
|
if (import_path.len == 0 or import_path[0] == '/') return .invalid_path
|
||||||
for (import_path) |byte| if (byte == 0) return .invalid_path
|
for (import_path) |byte| {
|
||||||
|
if (byte == 0) return .invalid_path
|
||||||
|
}
|
||||||
base := package_dir
|
base := package_dir
|
||||||
path := import_path
|
path := import_path
|
||||||
if (path[0] == '@') {
|
if (path[0] == '@') {
|
||||||
@@ -99,7 +110,8 @@ before proc(left, right []u8) bool {
|
|||||||
return left.len < right.len
|
return left.len < right.len
|
||||||
}
|
}
|
||||||
|
|
||||||
#! lists production sources in bytewise order; caller frees every path and the list.
|
#! lists production sources in bytewise order; free each path with mem.free using the same allocator.
|
||||||
|
#! release the list with arraylist.deinit, which uses its retained allocator; temporary directory storage is released before returning.
|
||||||
files proc(allocator mem.Allocator, package_dir []u8) arraylist.ArrayList([]u8) ! Error {
|
files proc(allocator mem.Allocator, package_dir []u8) arraylist.ArrayList([]u8) ! Error {
|
||||||
handle := try fs.open_directory(allocator, package_dir)
|
handle := try fs.open_directory(allocator, package_dir)
|
||||||
closing := false
|
closing := false
|
||||||
@@ -116,8 +128,11 @@ files proc(allocator mem.Allocator, package_dir []u8) arraylist.ArrayList([]u8)
|
|||||||
and entry.kind != .unknown
|
and entry.kind != .unknown
|
||||||
) continue
|
) continue
|
||||||
name :: entry.name
|
name :: entry.name
|
||||||
if ((!ends_with(name, ".hon") and !ends_with(name, ".bro"))
|
if (
|
||||||
or ends_with(name, ".test.hon") or ends_with(name, ".test.bro")) continue
|
(!ends_with(name, ".hon") and !ends_with(name, ".bro"))
|
||||||
|
or ends_with(name, ".test.hon")
|
||||||
|
or ends_with(name, ".test.bro")
|
||||||
|
) continue
|
||||||
path :: try join(allocator, package_dir, name)
|
path :: try join(allocator, package_dir, name)
|
||||||
errdefer mem.free(allocator, path)
|
errdefer mem.free(allocator, path)
|
||||||
if (entry.kind != .file and try fs.kind(allocator, path) != .file) {
|
if (entry.kind != .file and try fs.kind(allocator, path) != .file) {
|
||||||
@@ -157,6 +172,7 @@ sift proc(items []mut []u8, start, count usize) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#! the compiler limits source size to its u32 span address space; storage remains caller-owned.
|
#! the compiler limits source size to its u32 span address space; storage remains caller-owned.
|
||||||
|
#! release the returned list with arraylist.deinit, which uses its retained allocator.
|
||||||
read_source proc(allocator mem.Allocator, runtime io.Io, path []u8) arraylist.ArrayList(u8) ! Error {
|
read_source proc(allocator mem.Allocator, runtime io.Io, path []u8) arraylist.ArrayList(u8) ! Error {
|
||||||
return fs.read_file(allocator, runtime, path, usize(maxval!(u32))) catch |err| {
|
return fs.read_file(allocator, runtime, path, usize(maxval!(u32))) catch |err| {
|
||||||
match err {
|
match err {
|
||||||
|
|||||||
+32
-13
@@ -47,6 +47,7 @@ State :: struct {
|
|||||||
|
|
||||||
#! lowers checked source into caller-owned executable storage; release with ir.deinit.
|
#! lowers checked source into caller-owned executable storage; release with ir.deinit.
|
||||||
#! no frontend storage or source bytes are retained by the returned module.
|
#! no frontend storage or source bytes are retained by the returned module.
|
||||||
|
#! retains `allocator` in the module; temporary lowering storage is released before returning.
|
||||||
lower proc(
|
lower proc(
|
||||||
allocator mem.Allocator,
|
allocator mem.Allocator,
|
||||||
tree @ast.Tree,
|
tree @ast.Tree,
|
||||||
@@ -65,6 +66,7 @@ lower proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#! lowers the module arena, selecting an entry only from `root_package`.
|
#! lowers the module arena, selecting an entry only from `root_package`.
|
||||||
|
#! retains `allocator` in the returned module; release it with ir.deinit; temporary lowering storage is released before returning.
|
||||||
lower_package proc(
|
lower_package proc(
|
||||||
allocator mem.Allocator,
|
allocator mem.Allocator,
|
||||||
tree @ast.Tree,
|
tree @ast.Tree,
|
||||||
@@ -78,6 +80,7 @@ lower_package proc(
|
|||||||
|
|
||||||
#! lowers an isolated compile-time call wrapper without applying program entry-name rules.
|
#! lowers an isolated compile-time call wrapper without applying program entry-name rules.
|
||||||
#! the caller supplies a checked parameterless function and owns the returned IR.
|
#! the caller supplies a checked parameterless function and owns the returned IR.
|
||||||
|
#! retains `allocator` in the returned module; release it with ir.deinit; temporary lowering storage is released before returning.
|
||||||
lower_evaluation proc(
|
lower_evaluation proc(
|
||||||
allocator mem.Allocator,
|
allocator mem.Allocator,
|
||||||
tree @ast.Tree,
|
tree @ast.Tree,
|
||||||
@@ -392,7 +395,9 @@ lower_global proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diag
|
|||||||
state.module.functions.items[state.function].result_array_type = global.array_type
|
state.module.functions.items[state.function].result_array_type = global.array_type
|
||||||
if (trap_binding(state, id)) return
|
if (trap_binding(state, id)) return
|
||||||
declaration :: ast.statement_decl(state.tree, id)
|
declaration :: ast.statement_decl(state.tree, id)
|
||||||
if (declaration.type) |annotation| if (trap_type(state, annotation)) return
|
if (declaration.type) |annotation| {
|
||||||
|
if (trap_type(state, annotation)) return
|
||||||
|
}
|
||||||
value :: try lower_as(state, declaration.initializer, typecheck.type_of(state.checked, id))
|
value :: try lower_as(state, declaration.initializer, typecheck.type_of(state.checked, id))
|
||||||
if (state.terminated or trap_type(state, id)) return
|
if (state.terminated or trap_type(state, id)) return
|
||||||
finish(state, value, declaration.span)
|
finish(state, value, declaration.span)
|
||||||
@@ -792,10 +797,14 @@ lower_statement proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | d
|
|||||||
}
|
}
|
||||||
.assignment_stmt: {
|
.assignment_stmt: {
|
||||||
assignment :: ast.assignment_stmt(tree, id)
|
assignment :: ast.assignment_stmt(tree, id)
|
||||||
if (ast.expr_kind(tree, assignment.target) == .index
|
if (
|
||||||
|
ast.expr_kind(tree, assignment.target) == .index
|
||||||
or ast.expr_kind(tree, assignment.target) == .unary
|
or ast.expr_kind(tree, assignment.target) == .unary
|
||||||
or (ast.expr_kind(tree, assignment.target) == .selection
|
or (
|
||||||
and sema.binding(state.resolution, assignment.target).kind == .none)) {
|
ast.expr_kind(tree, assignment.target) == .selection
|
||||||
|
and sema.binding(state.resolution, assignment.target).kind == .none
|
||||||
|
)
|
||||||
|
) {
|
||||||
value :: try lower_as(state, assignment.value, typecheck.type_of(state.checked, assignment.target))
|
value :: try lower_as(state, assignment.value, typecheck.type_of(state.checked, assignment.target))
|
||||||
if (state.terminated) return
|
if (state.terminated) return
|
||||||
if (!is_place(state, assignment.target)) {
|
if (!is_place(state, assignment.target)) {
|
||||||
@@ -837,7 +846,9 @@ lower_statement proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | d
|
|||||||
if (ast.expr_kind(tree, ast.statement_decl(tree, id).initializer) == .alias_expr) return
|
if (ast.expr_kind(tree, ast.statement_decl(tree, id).initializer) == .alias_expr) return
|
||||||
if (trap_binding(state, id)) return
|
if (trap_binding(state, id)) return
|
||||||
declaration :: ast.statement_decl(tree, id)
|
declaration :: ast.statement_decl(tree, id)
|
||||||
if (declaration.type) |annotation| if (trap_type(state, annotation)) return
|
if (declaration.type) |annotation| {
|
||||||
|
if (trap_type(state, annotation)) return
|
||||||
|
}
|
||||||
value :: try lower_as(state, declaration.initializer, typecheck.type_of(state.checked, id))
|
value :: try lower_as(state, declaration.initializer, typecheck.type_of(state.checked, id))
|
||||||
if (state.terminated or trap_type(state, id)) return
|
if (state.terminated or trap_type(state, id)) return
|
||||||
_ = try emit(state, ir.Instruction{
|
_ = try emit(state, ir.Instruction{
|
||||||
@@ -1393,8 +1404,10 @@ convert_value proc(state @mut State, value ir.ValueId, actual, expected typechec
|
|||||||
length :: try usize_constant(state, typecheck.array_type(state.checked, actual.array_type).len, span)
|
length :: try usize_constant(state, typecheck.array_type(state.checked, actual.array_type).len, span)
|
||||||
return try record_pair(state, try array_shape(state, expected), pointer, length, span)
|
return try record_pair(state, try array_shape(state, expected), pointer, length, span)
|
||||||
}
|
}
|
||||||
if (!typecheck.error_assignable(state.checked, actual, expected)
|
if (
|
||||||
and !typecheck.error_assignable(state.checked, expected, actual)) return value
|
!typecheck.error_assignable(state.checked, actual, expected)
|
||||||
|
and !typecheck.error_assignable(state.checked, expected, actual)
|
||||||
|
) return value
|
||||||
source_union := false
|
source_union := false
|
||||||
if (actual.kind == .record) source_union = typecheck.record_type(state.checked, actual.record_type).is_union
|
if (actual.kind == .record) source_union = typecheck.record_type(state.checked, actual.record_type).is_union
|
||||||
if (expected.kind == .enumeration) {
|
if (expected.kind == .enumeration) {
|
||||||
@@ -1701,8 +1714,10 @@ lower_place proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError |
|
|||||||
if (state.terminated or trap_type(state, id)) return ir.no_value
|
if (state.terminated or trap_type(state, id)) return ir.no_value
|
||||||
return try pointer_address(state, pointer, checked, span)
|
return try pointer_address(state, pointer, checked, span)
|
||||||
}
|
}
|
||||||
if (ast.expr_kind(state.tree, id) == .selection
|
if (
|
||||||
and sema.binding(state.resolution, id).kind == .none) {
|
ast.expr_kind(state.tree, id) == .selection
|
||||||
|
and sema.binding(state.resolution, id).kind == .none
|
||||||
|
) {
|
||||||
expression :: ast.selection_expr(state.tree, id)
|
expression :: ast.selection_expr(state.tree, id)
|
||||||
base_type :: typecheck.type_of(state.checked, expression.base)
|
base_type :: typecheck.type_of(state.checked, expression.base)
|
||||||
base := ir.no_value
|
base := ir.no_value
|
||||||
@@ -1892,8 +1907,10 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
|||||||
return try lower_string(state, id)
|
return try lower_string(state, id)
|
||||||
}
|
}
|
||||||
.identifier, .selection: {
|
.identifier, .selection: {
|
||||||
if (ast.expr_kind(tree, id) == .selection
|
if (
|
||||||
and sema.binding(state.resolution, id).kind == .none) {
|
ast.expr_kind(tree, id) == .selection
|
||||||
|
and sema.binding(state.resolution, id).kind == .none
|
||||||
|
) {
|
||||||
selection :: ast.selection_expr(tree, id)
|
selection :: ast.selection_expr(tree, id)
|
||||||
base_type :: typecheck.type_of(state.checked, selection.base)
|
base_type :: typecheck.type_of(state.checked, selection.base)
|
||||||
if (base_type.kind == .slice or base_type.kind == .string or base_type.kind == .array) {
|
if (base_type.kind == .slice or base_type.kind == .string or base_type.kind == .array) {
|
||||||
@@ -2018,8 +2035,10 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
|||||||
}
|
}
|
||||||
left_type :: typecheck.type_of(state.checked, expression.left)
|
left_type :: typecheck.type_of(state.checked, expression.left)
|
||||||
right_type :: typecheck.type_of(state.checked, expression.right)
|
right_type :: typecheck.type_of(state.checked, expression.right)
|
||||||
if ((expression.operator == .equal or expression.operator == .not_equal)
|
if (
|
||||||
and (left_type.kind == .null or right_type.kind == .null)) {
|
(expression.operator == .equal or expression.operator == .not_equal)
|
||||||
|
and (left_type.kind == .null or right_type.kind == .null)
|
||||||
|
) {
|
||||||
optional_id :: if (left_type.kind == .null) expression.right else expression.left
|
optional_id :: if (left_type.kind == .null) expression.right else expression.left
|
||||||
value :: try lower_expression(state, optional_id)
|
value :: try lower_expression(state, optional_id)
|
||||||
if (state.terminated or trap_type(state, id)) return ir.no_value
|
if (state.terminated or trap_type(state, id)) return ir.no_value
|
||||||
|
|||||||
@@ -1174,10 +1174,13 @@ parse_record_field proc(state @mut State, declaration bool) ast.NodeId ! ParseEr
|
|||||||
name ?ast.NodeId := null
|
name ?ast.NodeId := null
|
||||||
type ?ast.NodeId := null
|
type ?ast.NodeId := null
|
||||||
value ?ast.NodeId := null
|
value ?ast.NodeId := null
|
||||||
named :: member_name(current(state).kind) and (
|
named :: (
|
||||||
|
member_name(current(state).kind)
|
||||||
|
and (
|
||||||
(!declaration and next_n_is(state, 1, .equal))
|
(!declaration and next_n_is(state, 1, .equal))
|
||||||
or (declaration and starts_type(state.tokens[state.next_token + 1].kind))
|
or (declaration and starts_type(state.tokens[state.next_token + 1].kind))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
if (named) {
|
if (named) {
|
||||||
token :: if (declaration) try consume(state, .ident) else try consume_member(state, false)
|
token :: if (declaration) try consume(state, .ident) else try consume_member(state, false)
|
||||||
name = try ast.add_identifier(state.builder, token.span, token.data.str_id)
|
name = try ast.add_identifier(state.builder, token.span, token.data.str_id)
|
||||||
@@ -1901,10 +1904,12 @@ consume proc(state @mut State, kind lexer.TokenKind) lexer.Token ! SyntaxError {
|
|||||||
@hide:file
|
@hide:file
|
||||||
consume_either proc(state @mut State, kinds []lexer.TokenKind) lexer.Token ! SyntaxError {
|
consume_either proc(state @mut State, kinds []lexer.TokenKind) lexer.Token ! SyntaxError {
|
||||||
token :: current(state)
|
token :: current(state)
|
||||||
for (kinds) |kind| if token.kind == kind {
|
for (kinds) |kind| {
|
||||||
|
if token.kind == kind {
|
||||||
state.next_token += 1
|
state.next_token += 1
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return .unexpected_token
|
return .unexpected_token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+255
-119
@@ -227,7 +227,7 @@ State :: struct {
|
|||||||
pointer_depth usize = 0
|
pointer_depth usize = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#! borrows resolution and diagnostics during checking; Result retains the AST borrow for signature comparisons.
|
#! borrows `resolution` and `diagnostic_store` during checking; Result retains the AST borrow for signature comparisons.
|
||||||
#! temporary constraints are freed on every exit; Result owns types, aggregate descriptors, initializer mappings, and fallthrough obligations.
|
#! temporary constraints are freed on every exit; Result owns types, aggregate descriptors, initializer mappings, and fallthrough obligations.
|
||||||
check proc(
|
check proc(
|
||||||
allocator mem.Allocator,
|
allocator mem.Allocator,
|
||||||
@@ -322,7 +322,7 @@ check_elaborated proc(
|
|||||||
try collect_declaration(&state, constraint)
|
try collect_declaration(&state, constraint)
|
||||||
} else try collect_expression(&state, constraint)
|
} else try collect_expression(&state, constraint)
|
||||||
}
|
}
|
||||||
# Preserve binding errors before RHS errors propagate. Indexed writability
|
# preserve binding errors before RHS errors propagate. indexed writability
|
||||||
# waits for shapes; neither check poisons the target's type.
|
# waits for shapes; neither check poisons the target's type.
|
||||||
for 0..count |i| {
|
for 0..count |i| {
|
||||||
if (cells[i].role != .assignment_stmt) continue
|
if (cells[i].role != .assignment_stmt) continue
|
||||||
@@ -357,8 +357,10 @@ check_elaborated proc(
|
|||||||
if (cells[i].role != .expression or !declaration_reference(tree, id)) continue
|
if (cells[i].role != .expression or !declaration_reference(tree, id)) continue
|
||||||
if (cells[i].type.kind == .invalid) continue
|
if (cells[i].type.kind == .invalid) continue
|
||||||
binding :: sema.binding(resolution, id)
|
binding :: sema.binding(resolution, id)
|
||||||
if (binding.kind == .declaration and !cells[i].assignment_value
|
if (
|
||||||
and !cells[usize(binding.data.declaration)].fixed and !has_narrowing(&state, id)) {
|
binding.kind == .declaration and !cells[i].assignment_value
|
||||||
|
and !cells[usize(binding.data.declaration)].fixed and !has_narrowing(&state, id)
|
||||||
|
) {
|
||||||
join(&state, id, binding.data.declaration)
|
join(&state, id, binding.data.declaration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,7 +373,8 @@ check_elaborated proc(
|
|||||||
if (assignment_values) {
|
if (assignment_values) {
|
||||||
for (0..count) |i| {
|
for (0..count) |i| {
|
||||||
id :: ast.NodeId(i)
|
id :: ast.NodeId(i)
|
||||||
if (!cells[i].assignment_value
|
if (
|
||||||
|
!cells[i].assignment_value
|
||||||
or cells[i].role != .expression
|
or cells[i].role != .expression
|
||||||
or !declaration_reference(tree, id)
|
or !declaration_reference(tree, id)
|
||||||
) continue
|
) continue
|
||||||
@@ -380,7 +383,7 @@ check_elaborated proc(
|
|||||||
if (binding.kind == .declaration) cells[i].type = types[usize(binding.data.declaration)]
|
if (binding.kind == .declaration) cells[i].type = types[usize(binding.data.declaration)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Calls use the signature result before contextual literal inference.
|
# calls use the signature result before contextual literal inference.
|
||||||
try prepare_optional_captures(&state, false)
|
try prepare_optional_captures(&state, false)
|
||||||
for (0..count) |i| {
|
for (0..count) |i| {
|
||||||
id :: ast.NodeId(i)
|
id :: ast.NodeId(i)
|
||||||
@@ -840,9 +843,13 @@ check_annotation proc(state @mut State, id ast.NodeId, allow_void bool) Type ! (
|
|||||||
view :: ast.array_type(state.tree, id)
|
view :: ast.array_type(state.tree, id)
|
||||||
element := try check_annotation(state, view.element, true)
|
element := try check_annotation(state, view.element, true)
|
||||||
element_binding :: sema.binding(state.resolution, view.element)
|
element_binding :: sema.binding(state.resolution, view.element)
|
||||||
if ((element_binding.kind == .builtin
|
if (
|
||||||
and (element_binding.data.builtin == .int or element_binding.data.builtin == .float))
|
(
|
||||||
or element.kind == .void or element.kind == .range) {
|
element_binding.kind == .builtin
|
||||||
|
and (element_binding.data.builtin == .int or element_binding.data.builtin == .float)
|
||||||
|
)
|
||||||
|
or element.kind == .void or element.kind == .range
|
||||||
|
) {
|
||||||
element = invalid_type(try diagnostics.add(state.diagnostics, .invalid_array_element_type, ast.node_span(state.tree, view.element)))
|
element = invalid_type(try diagnostics.add(state.diagnostics, .invalid_array_element_type, ast.node_span(state.tree, view.element)))
|
||||||
state.types[usize(view.element)] = element
|
state.types[usize(view.element)] = element
|
||||||
state.cells[usize(view.element)].type = element
|
state.cells[usize(view.element)].type = element
|
||||||
@@ -920,9 +927,11 @@ collect_declaration proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
|||||||
state.cells[usize(id)].type = invalid_type(binding.data.diagnostic)
|
state.cells[usize(id)].type = invalid_type(binding.data.diagnostic)
|
||||||
}
|
}
|
||||||
try collect_expression(state, declaration.initializer)
|
try collect_expression(state, declaration.initializer)
|
||||||
if (type_syntax(ast.expr_kind(state.tree, declaration.initializer))
|
if (
|
||||||
|
type_syntax(ast.expr_kind(state.tree, declaration.initializer))
|
||||||
and state.cells[usize(id)].type.kind != .invalid
|
and state.cells[usize(id)].type.kind != .invalid
|
||||||
and state.cells[usize(declaration.initializer)].type.kind != .none) {
|
and state.cells[usize(declaration.initializer)].type.kind != .none
|
||||||
|
) {
|
||||||
state.cells[usize(id)].fixed = true
|
state.cells[usize(id)].fixed = true
|
||||||
state.cells[usize(id)].type = state.cells[usize(declaration.initializer)].type
|
state.cells[usize(id)].type = state.cells[usize(declaration.initializer)].type
|
||||||
}
|
}
|
||||||
@@ -1208,8 +1217,10 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
|||||||
base :: ast.selection_expr(tree, id).base
|
base :: ast.selection_expr(tree, id).base
|
||||||
if (type_reference(state, base)) {
|
if (type_reference(state, base)) {
|
||||||
checked :: try check_annotation(state, base, true)
|
checked :: try check_annotation(state, base, true)
|
||||||
if (checked.kind != .invalid
|
if (
|
||||||
and (checked.kind != .enumeration or checked.nominal != ast.NodeId(maxval!(u32)))) {
|
checked.kind != .invalid
|
||||||
|
and (checked.kind != .enumeration or checked.nominal != ast.NodeId(maxval!(u32)))
|
||||||
|
) {
|
||||||
try fail(state, base, .type_not_value, ast.node_span(tree, base))
|
try fail(state, base, .type_not_value, ast.node_span(tree, base))
|
||||||
state.cells[usize(base)].type = state.types[usize(base)]
|
state.cells[usize(base)].type = state.types[usize(base)]
|
||||||
}
|
}
|
||||||
@@ -1219,8 +1230,10 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
|||||||
if (binding.kind == .declaration) {
|
if (binding.kind == .declaration) {
|
||||||
target :: binding.data.declaration
|
target :: binding.data.declaration
|
||||||
role :: state.cells[usize(target)].role
|
role :: state.cells[usize(target)].role
|
||||||
if (role != .parameter and role != .loop_capture and ast.decl_kind(tree, target) == .statement
|
if (
|
||||||
and type_syntax(ast.expr_kind(tree, ast.statement_decl(tree, target).initializer))) {
|
role != .parameter and role != .loop_capture and ast.decl_kind(tree, target) == .statement
|
||||||
|
and type_syntax(ast.expr_kind(tree, ast.statement_decl(tree, target).initializer))
|
||||||
|
) {
|
||||||
state.cells[usize(id)].type = invalid_type(try diagnostics.add(state.diagnostics, .type_not_value, ast.node_span(tree, id)))
|
state.cells[usize(id)].type = invalid_type(try diagnostics.add(state.diagnostics, .type_not_value, ast.node_span(tree, id)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1451,11 +1464,13 @@ bool_equality proc(state @mut State, id ast.NodeId) bool {
|
|||||||
right :: group_type(state, expression.right)
|
right :: group_type(state, expression.right)
|
||||||
return (
|
return (
|
||||||
(expression.operator == .equal or expression.operator == .not_equal)
|
(expression.operator == .equal or expression.operator == .not_equal)
|
||||||
and ((left.kind == .bool and right.kind == .bool)
|
and (
|
||||||
|
(left.kind == .bool and right.kind == .bool)
|
||||||
or left.kind == .enumeration or right.kind == .enumeration
|
or left.kind == .enumeration or right.kind == .enumeration
|
||||||
or left.kind == .pointer or right.kind == .pointer
|
or left.kind == .pointer or right.kind == .pointer
|
||||||
or left.kind == .optional or right.kind == .optional
|
or left.kind == .optional or right.kind == .optional
|
||||||
or left.kind == .null or right.kind == .null)
|
or left.kind == .null or right.kind == .null
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1534,8 +1549,10 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
|||||||
if (expected.kind == .fallible) {
|
if (expected.kind == .fallible) {
|
||||||
descriptor :: state.arrays.items[usize(expected.array_type)]
|
descriptor :: state.arrays.items[usize(expected.array_type)]
|
||||||
actual := group_type(state, id)
|
actual := group_type(state, id)
|
||||||
if (state.cells[usize(id)].role == .expression and declaration_reference(state.tree, id)
|
if (
|
||||||
and has_narrowing(state, id)) actual = try infer_shape(state, id, false)
|
state.cells[usize(id)].role == .expression and declaration_reference(state.tree, id)
|
||||||
|
and has_narrowing(state, id)
|
||||||
|
) actual = try infer_shape(state, id, false)
|
||||||
if (actual.kind == .fallible) return
|
if (actual.kind == .fallible) return
|
||||||
target := descriptor.element
|
target := descriptor.element
|
||||||
literal :: state.cells[index].literal
|
literal :: state.cells[index].literal
|
||||||
@@ -1547,16 +1564,24 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
|||||||
if (ast.record_literal(state.tree, literal).type) |annotation| {
|
if (ast.record_literal(state.tree, literal).type) |annotation| {
|
||||||
if (ast.expr_kind(state.tree, annotation) == .enum_literal) name = ast.enum_literal(state.tree, annotation)
|
if (ast.expr_kind(state.tree, annotation) == .enum_literal) name = ast.enum_literal(state.tree, annotation)
|
||||||
}
|
}
|
||||||
if (name == ast.NodeId(maxval!(u32)) and actual.kind == .none
|
if (
|
||||||
|
name == ast.NodeId(maxval!(u32)) and actual.kind == .none
|
||||||
and descriptor.element.kind != .record and descriptor.error_type.kind == .record
|
and descriptor.element.kind != .record and descriptor.error_type.kind == .record
|
||||||
and !state.records.items[usize(descriptor.error_type.record_type)].is_union) target = descriptor.error_type
|
and !state.records.items[usize(descriptor.error_type.record_type)].is_union
|
||||||
|
) target = descriptor.error_type
|
||||||
}
|
}
|
||||||
if (name != ast.NodeId(maxval!(u32)) and type_has_variant(state, descriptor.error_type, name)
|
if (
|
||||||
and !type_has_variant(state, descriptor.element, name)) target = descriptor.error_type
|
name != ast.NodeId(maxval!(u32)) and type_has_variant(state, descriptor.error_type, name)
|
||||||
else if (actual.kind != .none and !assignable_type(state, actual, target)
|
and !type_has_variant(state, descriptor.element, name)
|
||||||
and assignable_type(state, actual, descriptor.error_type)) target = descriptor.error_type
|
) target = descriptor.error_type
|
||||||
} else if (actual.kind != .none and !assignable_type(state, actual, target)
|
else if (
|
||||||
and assignable_type(state, actual, descriptor.error_type)) target = descriptor.error_type
|
actual.kind != .none and !assignable_type(state, actual, target)
|
||||||
|
and assignable_type(state, actual, descriptor.error_type)
|
||||||
|
) target = descriptor.error_type
|
||||||
|
} else if (
|
||||||
|
actual.kind != .none and !assignable_type(state, actual, target)
|
||||||
|
and assignable_type(state, actual, descriptor.error_type)
|
||||||
|
) target = descriptor.error_type
|
||||||
try demand(state, id, target, code)
|
try demand(state, id, target, code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1587,9 +1612,11 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (expected.kind == .pointer and state.cells[usize(id)].role == .expression
|
if (
|
||||||
|
expected.kind == .pointer and state.cells[usize(id)].role == .expression
|
||||||
and ast.expr_kind(state.tree, id) == .unary
|
and ast.expr_kind(state.tree, id) == .unary
|
||||||
and ast.unary_expr(state.tree, id).operator == .address_of) {
|
and ast.unary_expr(state.tree, id).operator == .address_of
|
||||||
|
) {
|
||||||
state.cells[index].expected = expected
|
state.cells[index].expected = expected
|
||||||
try demand(state, ast.unary_expr(state.tree, id).operand,
|
try demand(state, ast.unary_expr(state.tree, id).operand,
|
||||||
state.pointers.items[usize(expected.pointer_type)].element, code)
|
state.pointers.items[usize(expected.pointer_type)].element, code)
|
||||||
@@ -1604,8 +1631,10 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
literal_node :: state.cells[index].literal
|
literal_node :: state.cells[index].literal
|
||||||
if (expected.nominal != ast.NodeId(maxval!(u32))
|
if (
|
||||||
and (expected.kind == .record or expected.kind == .array)) return
|
expected.nominal != ast.NodeId(maxval!(u32))
|
||||||
|
and (expected.kind == .record or expected.kind == .array)
|
||||||
|
) return
|
||||||
if (literal_node != ast.NodeId(maxval!(u32)) and ast.expr_kind(state.tree, literal_node) == .enum_literal) {
|
if (literal_node != ast.NodeId(maxval!(u32)) and ast.expr_kind(state.tree, literal_node) == .enum_literal) {
|
||||||
previous :: state.cells[index].expected
|
previous :: state.cells[index].expected
|
||||||
if (previous.kind != .none and !same_type(state, previous, expected)) {
|
if (previous.kind != .none and !same_type(state, previous, expected)) {
|
||||||
@@ -1655,8 +1684,10 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
|||||||
if (!numeric_kind(expected.kind)) return
|
if (!numeric_kind(expected.kind)) return
|
||||||
if (!state.cells[index].numeric and state.cells[index].type.kind != .none) return
|
if (!state.cells[index].numeric and state.cells[index].type.kind != .none) return
|
||||||
state.cells[index].numeric = true
|
state.cells[index].numeric = true
|
||||||
if (state.cells[index].expected.kind != .none
|
if (
|
||||||
and !same_type(state, state.cells[index].expected, expected)) {
|
state.cells[index].expected.kind != .none
|
||||||
|
and !same_type(state, state.cells[index].expected, expected)
|
||||||
|
) {
|
||||||
state.cells[index].type = invalid_type(try diagnostics.add(state.diagnostics, code, ast.node_span(state.tree, id)))
|
state.cells[index].type = invalid_type(try diagnostics.add(state.diagnostics, code, ast.node_span(state.tree, id)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1706,8 +1737,13 @@ mutable_target proc(state @mut State, id ast.NodeId, allow_global bool) bool {
|
|||||||
base :: group_type(state, view.base)
|
base :: group_type(state, view.base)
|
||||||
if (base.kind == .pointer) {
|
if (base.kind == .pointer) {
|
||||||
pointer :: state.pointers.items[usize(base.pointer_type)]
|
pointer :: state.pointers.items[usize(base.pointer_type)]
|
||||||
return pointer.mutable and (pointer.many
|
return (
|
||||||
or (pointer.element.kind == .array and state.arrays.items[usize(pointer.element.array_type)].mutable))
|
pointer.mutable
|
||||||
|
and (
|
||||||
|
pointer.many
|
||||||
|
or (pointer.element.kind == .array and state.arrays.items[usize(pointer.element.array_type)].mutable)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (base.kind == .slice) return state.arrays.items[usize(base.array_type)].mutable
|
if (base.kind == .slice) return state.arrays.items[usize(base.array_type)].mutable
|
||||||
return base.kind == .array and state.arrays.items[usize(base.array_type)].mutable and mutable_target(state, view.base, allow_global)
|
return base.kind == .array and state.arrays.items[usize(base.array_type)].mutable and mutable_target(state, view.base, allow_global)
|
||||||
@@ -1798,8 +1834,10 @@ assignable_type proc(state @State, actual, expected Type) bool {
|
|||||||
or error_subset(state.records.items, state.enums.items, actual, target.error_type)
|
or error_subset(state.records.items, state.enums.items, actual, target.error_type)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if ((composed_type(state, actual) or composed_type(state, expected))
|
if (
|
||||||
and error_subset(state.records.items, state.enums.items, actual, expected)) return true
|
(composed_type(state, actual) or composed_type(state, expected))
|
||||||
|
and error_subset(state.records.items, state.enums.items, actual, expected)
|
||||||
|
) return true
|
||||||
if (expected.kind == .optional) {
|
if (expected.kind == .optional) {
|
||||||
child :: state.arrays.items[usize(expected.array_type)].element
|
child :: state.arrays.items[usize(expected.array_type)].element
|
||||||
if (actual.kind == .null) return true
|
if (actual.kind == .null) return true
|
||||||
@@ -1851,9 +1889,11 @@ assignable_value proc(state @mut State, value ast.NodeId, actual, expected Type)
|
|||||||
if (!assignable_type(state, actual, expected)) return false
|
if (!assignable_type(state, actual, expected)) return false
|
||||||
target := expected
|
target := expected
|
||||||
while (target.kind == .optional) target = state.arrays.items[usize(target.array_type)].element
|
while (target.kind == .optional) target = state.arrays.items[usize(target.array_type)].element
|
||||||
if (actual.kind == .array and target.kind == .slice
|
if (
|
||||||
|
actual.kind == .array and target.kind == .slice
|
||||||
and state.arrays.items[usize(target.array_type)].mutable
|
and state.arrays.items[usize(target.array_type)].mutable
|
||||||
and addressable_target(state, value)) return mutable_target(state, value, true)
|
and addressable_target(state, value)
|
||||||
|
) return mutable_target(state, value, true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1907,8 +1947,10 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
|
|||||||
try fail(state, value, .return_type_mismatch, ast.node_span(tree, value))
|
try fail(state, value, .return_type_mismatch, ast.node_span(tree, value))
|
||||||
state.types[usize(id)] = state.types[usize(value)]
|
state.types[usize(id)] = state.types[usize(value)]
|
||||||
}
|
}
|
||||||
} else if (expected.kind != .void
|
} else if (
|
||||||
and !(expected.kind == .fallible and state.arrays.items[usize(expected.array_type)].element.kind == .void)) {
|
expected.kind != .void
|
||||||
|
and !(expected.kind == .fallible and state.arrays.items[usize(expected.array_type)].element.kind == .void)
|
||||||
|
) {
|
||||||
try fail(state, id, .return_type_mismatch, statement.span)
|
try fail(state, id, .return_type_mismatch, statement.span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1939,8 +1981,10 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (expression.operator == .dereference) {
|
if (expression.operator == .dereference) {
|
||||||
if (state.types[usize(expression.operand)].kind != .invalid
|
if (
|
||||||
and state.types[usize(expression.operand)].kind != .pointer) {
|
state.types[usize(expression.operand)].kind != .invalid
|
||||||
|
and state.types[usize(expression.operand)].kind != .pointer
|
||||||
|
) {
|
||||||
try fail(state, id, .invalid_dereference, expression.span)
|
try fail(state, id, .invalid_dereference, expression.span)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -1966,11 +2010,15 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ((expression.operator == .equal or expression.operator == .not_equal)
|
if (
|
||||||
and ((left.kind == .optional and right.kind == .null) or (left.kind == .null and right.kind == .optional))) return
|
(expression.operator == .equal or expression.operator == .not_equal)
|
||||||
if ((expression.operator == .equal or expression.operator == .not_equal)
|
and ((left.kind == .optional and right.kind == .null) or (left.kind == .null and right.kind == .optional))
|
||||||
|
) return
|
||||||
|
if (
|
||||||
|
(expression.operator == .equal or expression.operator == .not_equal)
|
||||||
and (left.kind == .bool or left.kind == .enumeration or left.kind == .pointer)
|
and (left.kind == .bool or left.kind == .enumeration or left.kind == .pointer)
|
||||||
and (assignable_type(state, left, right) or assignable_type(state, right, left))) return
|
and (assignable_type(state, left, right) or assignable_type(state, right, left))
|
||||||
|
) return
|
||||||
if (!numeric_kind(left.kind) or !numeric_kind(right.kind) or !same_type(state, left, right)) {
|
if (!numeric_kind(left.kind) or !numeric_kind(right.kind) or !same_type(state, left, right)) {
|
||||||
try fail(state, id, .invalid_operand, expression.span)
|
try fail(state, id, .invalid_operand, expression.span)
|
||||||
} else if (expression.operator == .divide and left.kind != .float) {
|
} else if (expression.operator == .divide and left.kind != .float) {
|
||||||
@@ -2066,9 +2114,13 @@ validate_call proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | dia
|
|||||||
and state.enums.items[usize(actual.enum_type)].explicit_backing
|
and state.enums.items[usize(actual.enum_type)].explicit_backing
|
||||||
)
|
)
|
||||||
backing :: immediate_backing(state, actual)
|
backing :: immediate_backing(state, actual)
|
||||||
if (actual.kind != .invalid
|
if (
|
||||||
and (!numeric_kind(backing.kind) and !allowed_enum
|
actual.kind != .invalid
|
||||||
or backing.nominal != ast.NodeId(maxval!(u32)))) {
|
and (
|
||||||
|
!numeric_kind(backing.kind) and !allowed_enum
|
||||||
|
or backing.nominal != ast.NodeId(maxval!(u32))
|
||||||
|
)
|
||||||
|
) {
|
||||||
try fail(state, argument, .invalid_operand, ast.node_span(tree, argument))
|
try fail(state, argument, .invalid_operand, ast.node_span(tree, argument))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2167,14 +2219,18 @@ evaluate_integer proc(state @mut State, id ast.NodeId, allow_local bool) ?Consta
|
|||||||
binding :: sema.binding(state.resolution, id)
|
binding :: sema.binding(state.resolution, id)
|
||||||
if (binding.kind != .declaration) return null
|
if (binding.kind != .declaration) return null
|
||||||
target :: binding.data.declaration
|
target :: binding.data.declaration
|
||||||
if (!state.cells[usize(target)].count_declaration
|
if (
|
||||||
and (!allow_local or !state.cells[usize(target)].local)) return null
|
!state.cells[usize(target)].count_declaration
|
||||||
|
and (!allow_local or !state.cells[usize(target)].local)
|
||||||
|
) return null
|
||||||
declaration :: ast.statement_decl(state.tree, target)
|
declaration :: ast.statement_decl(state.tree, target)
|
||||||
if (declaration.mutability != .constant) return null
|
if (declaration.mutability != .constant) return null
|
||||||
if (declaration.type) |annotation| {
|
if (declaration.type) |annotation| {
|
||||||
type_binding :: sema.binding(state.resolution, annotation)
|
type_binding :: sema.binding(state.resolution, annotation)
|
||||||
if (!integer_kind(state.types[usize(annotation)].kind)
|
if (
|
||||||
and (type_binding.kind != .builtin or !integer_kind(builtin_kind(type_binding.data.builtin)))) return null
|
!integer_kind(state.types[usize(annotation)].kind)
|
||||||
|
and (type_binding.kind != .builtin or !integer_kind(builtin_kind(type_binding.data.builtin)))
|
||||||
|
) return null
|
||||||
}
|
}
|
||||||
return evaluate_integer(state, declaration.initializer, allow_local)
|
return evaluate_integer(state, declaration.initializer, allow_local)
|
||||||
}
|
}
|
||||||
@@ -2224,8 +2280,10 @@ evaluate_integer proc(state @mut State, id ast.NodeId, allow_local bool) ?Consta
|
|||||||
@hide:file
|
@hide:file
|
||||||
intern_pointer proc(state @mut State, descriptor PointerType) Type ! mem.AllocError {
|
intern_pointer proc(state @mut State, descriptor PointerType) Type ! mem.AllocError {
|
||||||
for state.pointers.items |existing, i| {
|
for state.pointers.items |existing, i| {
|
||||||
if (existing.mutable == descriptor.mutable and existing.many == descriptor.many
|
if (
|
||||||
and same_type(state, existing.element, descriptor.element)) {
|
existing.mutable == descriptor.mutable and existing.many == descriptor.many
|
||||||
|
and same_type(state, existing.element, descriptor.element)
|
||||||
|
) {
|
||||||
return Type{ kind = .pointer, pointer_type = PointerId(i) }
|
return Type{ kind = .pointer, pointer_type = PointerId(i) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2241,10 +2299,12 @@ check_pointer_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
|
|||||||
element :: try check_annotation(state, view.element, true)
|
element :: try check_annotation(state, view.element, true)
|
||||||
if (element.kind == .invalid) return element
|
if (element.kind == .invalid) return element
|
||||||
binding :: sema.binding(state.resolution, view.element)
|
binding :: sema.binding(state.resolution, view.element)
|
||||||
if (element.kind == .none or element.kind == .void or element.kind == .range
|
if (
|
||||||
|
element.kind == .none or element.kind == .void or element.kind == .range
|
||||||
or element.kind == .type_value
|
or element.kind == .type_value
|
||||||
or has_inferred_count(state, element)
|
or has_inferred_count(state, element)
|
||||||
or (binding.kind == .builtin and (binding.data.builtin == .int or binding.data.builtin == .float))) {
|
or (binding.kind == .builtin and (binding.data.builtin == .int or binding.data.builtin == .float))
|
||||||
|
) {
|
||||||
return invalid_type(try diagnostics.add(state.diagnostics, .invalid_pointer_type, ast.node_span(state.tree, id)))
|
return invalid_type(try diagnostics.add(state.diagnostics, .invalid_pointer_type, ast.node_span(state.tree, id)))
|
||||||
}
|
}
|
||||||
return try intern_pointer(state, PointerType{ element = element, mutable = view.mutable, many = view.many })
|
return try intern_pointer(state, PointerType{ element = element, mutable = view.mutable, many = view.many })
|
||||||
@@ -2282,9 +2342,11 @@ check_function_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
|
|||||||
@hide:file
|
@hide:file
|
||||||
intern_array proc(state @mut State, descriptor ArrayType) Type ! mem.AllocError {
|
intern_array proc(state @mut State, descriptor ArrayType) Type ! mem.AllocError {
|
||||||
for state.arrays.items |existing, i| {
|
for state.arrays.items |existing, i| {
|
||||||
if (existing.kind == descriptor.kind and existing.len == descriptor.len and existing.mutable == descriptor.mutable
|
if (
|
||||||
|
existing.kind == descriptor.kind and existing.len == descriptor.len and existing.mutable == descriptor.mutable
|
||||||
and same_sentinel(existing.sentinel, descriptor.sentinel) and same_type(state, existing.element, descriptor.element)
|
and same_sentinel(existing.sentinel, descriptor.sentinel) and same_type(state, existing.element, descriptor.element)
|
||||||
and same_type(state, existing.error_type, descriptor.error_type)) {
|
and same_type(state, existing.error_type, descriptor.error_type)
|
||||||
|
) {
|
||||||
return Type{ kind = descriptor.kind, array_type = ArrayId(i) }
|
return Type{ kind = descriptor.kind, array_type = ArrayId(i) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2527,8 +2589,10 @@ same_type proc(state @State, left, right Type) bool {
|
|||||||
and error_subset(state.records.items, state.enums.items, right, left)
|
and error_subset(state.records.items, state.enums.items, right, left)
|
||||||
)
|
)
|
||||||
if (a.is_union or b.is_union) return false
|
if (a.is_union or b.is_union) return false
|
||||||
if (a.declaration != ast.NodeId(maxval!(u32)) or b.declaration != ast.NodeId(maxval!(u32))
|
if (
|
||||||
or a.fields.len != b.fields.len) return false
|
a.declaration != ast.NodeId(maxval!(u32)) or b.declaration != ast.NodeId(maxval!(u32))
|
||||||
|
or a.fields.len != b.fields.len
|
||||||
|
) return false
|
||||||
for a.fields |field, i| {
|
for a.fields |field, i| {
|
||||||
other :: b.fields[i]
|
other :: b.fields[i]
|
||||||
if ((field.name == null) != (other.name == null)) return false
|
if ((field.name == null) != (other.name == null)) return false
|
||||||
@@ -2749,9 +2813,11 @@ infer_shape proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.cells[usize(id)].role == .expression
|
if (
|
||||||
|
state.cells[usize(id)].role == .expression
|
||||||
and ast.expr_kind(state.tree, id) == .selection
|
and ast.expr_kind(state.tree, id) == .selection
|
||||||
and sema.binding(state.resolution, id).kind == .none) {
|
and sema.binding(state.resolution, id).kind == .none
|
||||||
|
) {
|
||||||
view :: ast.selection_expr(state.tree, id)
|
view :: ast.selection_expr(state.tree, id)
|
||||||
if (type_reference(state, view.base)) {
|
if (type_reference(state, view.base)) {
|
||||||
enum_shape :: try check_annotation(state, view.base, false)
|
enum_shape :: try check_annotation(state, view.base, false)
|
||||||
@@ -2777,8 +2843,10 @@ infer_shape proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
|||||||
}
|
}
|
||||||
if (base.kind == .pointer and base.nominal == ast.NodeId(maxval!(u32))) {
|
if (base.kind == .pointer and base.nominal == ast.NodeId(maxval!(u32))) {
|
||||||
pointer :: state.pointers.items[usize(base.pointer_type)]
|
pointer :: state.pointers.items[usize(base.pointer_type)]
|
||||||
if (!pointer.many and pointer.element.kind == .array
|
if (
|
||||||
and ast.expr_kind(state.tree, view.member) == .identifier) {
|
!pointer.many and pointer.element.kind == .array
|
||||||
|
and ast.expr_kind(state.tree, view.member) == .identifier
|
||||||
|
) {
|
||||||
member :: ast.source_text(state.tree, ast.node_span(state.tree, view.member))
|
member :: ast.source_text(state.tree, ast.node_span(state.tree, view.member))
|
||||||
if (mem.eql(member, "len")) {
|
if (mem.eql(member, "len")) {
|
||||||
type :: Type{ kind = .usize }
|
type :: Type{ kind = .usize }
|
||||||
@@ -2837,8 +2905,10 @@ infer_shape proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
|||||||
binding :: sema.binding(state.resolution, id)
|
binding :: sema.binding(state.resolution, id)
|
||||||
if (binding.kind == .declaration) {
|
if (binding.kind == .declaration) {
|
||||||
target :: binding.data.declaration
|
target :: binding.data.declaration
|
||||||
if (state.cells[usize(target)].role == .declaration and state.cells[usize(target)].fixed
|
if (
|
||||||
and has_inferred_count(state, state.cells[usize(target)].type)) {
|
state.cells[usize(target)].role == .declaration and state.cells[usize(target)].fixed
|
||||||
|
and has_inferred_count(state, state.cells[usize(target)].type)
|
||||||
|
) {
|
||||||
declaration :: ast.statement_decl(state.tree, target)
|
declaration :: ast.statement_decl(state.tree, target)
|
||||||
actual :: try infer_shape(state, declaration.initializer, defaults)
|
actual :: try infer_shape(state, declaration.initializer, defaults)
|
||||||
if (actual.kind == .array and !has_inferred_count(state, actual)) {
|
if (actual.kind == .array and !has_inferred_count(state, actual)) {
|
||||||
@@ -2931,8 +3001,10 @@ prepare_optional_captures proc(state @mut State, defaults bool) void ! (mem.Allo
|
|||||||
for (0..(state.cells.len)) |j| {
|
for (0..(state.cells.len)) |j| {
|
||||||
if (state.cells[j].role != .expression or !declaration_reference(state.tree, ast.NodeId(j))) continue
|
if (state.cells[j].role != .expression or !declaration_reference(state.tree, ast.NodeId(j))) continue
|
||||||
binding :: sema.binding(state.resolution, ast.NodeId(j))
|
binding :: sema.binding(state.resolution, ast.NodeId(j))
|
||||||
if (binding.kind == .declaration and binding.data.declaration == capture
|
if (
|
||||||
and !has_narrowing(state, ast.NodeId(j))) seed(state, ast.NodeId(j), child)
|
binding.kind == .declaration and binding.data.declaration == capture
|
||||||
|
and !has_narrowing(state, ast.NodeId(j))
|
||||||
|
) seed(state, ast.NodeId(j), child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2999,8 +3071,10 @@ apply_contexts proc(state @mut State, assignment_values, early bool) void ! (mem
|
|||||||
if (state.cells[i].role != .expression or state.cells[i].assignment_value != assignment_values or ast.expr_kind(tree, id) != .call) continue
|
if (state.cells[i].role != .expression or state.cells[i].assignment_value != assignment_values or ast.expr_kind(tree, id) != .call) continue
|
||||||
if (state.backings[i].kind != .none) {
|
if (state.backings[i].kind != .none) {
|
||||||
backing :: state.backings[i]
|
backing :: state.backings[i]
|
||||||
if (ast.call_arg_count(tree, id) == 1
|
if (
|
||||||
and (backing.nominal != ast.NodeId(maxval!(u32)) or !numeric_kind(backing.kind))) {
|
ast.call_arg_count(tree, id) == 1
|
||||||
|
and (backing.nominal != ast.NodeId(maxval!(u32)) or !numeric_kind(backing.kind))
|
||||||
|
) {
|
||||||
try context_demand(state, ast.call_arg(tree, id, 0), backing, .type_mismatch, early)
|
try context_demand(state, ast.call_arg(tree, id, 0), backing, .type_mismatch, early)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -3029,9 +3103,11 @@ apply_contexts proc(state @mut State, assignment_values, early bool) void ! (mem
|
|||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
context_demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Code, early bool) void ! (mem.AllocError | diagnostics.CapacityError) {
|
context_demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Code, early bool) void ! (mem.AllocError | diagnostics.CapacityError) {
|
||||||
if (early and expected.kind != .array and expected.kind != .record
|
if (
|
||||||
|
early and expected.kind != .array and expected.kind != .record
|
||||||
and expected.kind != .pointer and expected.kind != .slice and expected.kind != .optional and expected.kind != .fallible
|
and expected.kind != .pointer and expected.kind != .slice and expected.kind != .optional and expected.kind != .fallible
|
||||||
and expected.kind != .enumeration and !integer_kind(expected.kind)) return
|
and expected.kind != .enumeration and !integer_kind(expected.kind)
|
||||||
|
) return
|
||||||
try demand(state, id, expected, code)
|
try demand(state, id, expected, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3080,13 +3156,16 @@ prepare_ranges proc(state @mut State, assignment_values, constrain bool) void !
|
|||||||
}
|
}
|
||||||
for 0..(state.cells.len) |i| {
|
for 0..(state.cells.len) |i| {
|
||||||
id :: ast.NodeId(i)
|
id :: ast.NodeId(i)
|
||||||
if (state.cells[i].role != .expression
|
if (
|
||||||
|
state.cells[i].role != .expression
|
||||||
or state.cells[i].assignment_value
|
or state.cells[i].assignment_value
|
||||||
or !declaration_reference(tree, id)
|
or !declaration_reference(tree, id)
|
||||||
) continue
|
) continue
|
||||||
binding :: sema.binding(state.resolution, id)
|
binding :: sema.binding(state.resolution, id)
|
||||||
if (binding.kind == .declaration and state.cells[usize(binding.data.declaration)].role == .loop_capture
|
if (
|
||||||
and !has_narrowing(state, id)) {
|
binding.kind == .declaration and state.cells[usize(binding.data.declaration)].role == .loop_capture
|
||||||
|
and !has_narrowing(state, id)
|
||||||
|
) {
|
||||||
seed(state, id, group_type(state, binding.data.declaration))
|
seed(state, id, group_type(state, binding.data.declaration))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3142,13 +3221,16 @@ specialize_annotations proc(state @mut State) void ! (mem.AllocError | diagnosti
|
|||||||
}
|
}
|
||||||
for 0..(state.cells.len) |i| {
|
for 0..(state.cells.len) |i| {
|
||||||
id :: ast.NodeId(i)
|
id :: ast.NodeId(i)
|
||||||
if (state.cells[i].role != .expression
|
if (
|
||||||
|
state.cells[i].role != .expression
|
||||||
or state.cells[i].assignment_value
|
or state.cells[i].assignment_value
|
||||||
or !declaration_reference(state.tree, id)
|
or !declaration_reference(state.tree, id)
|
||||||
) continue
|
) continue
|
||||||
binding :: sema.binding(state.resolution, id)
|
binding :: sema.binding(state.resolution, id)
|
||||||
if (binding.kind == .declaration and state.cells[usize(binding.data.declaration)].fixed
|
if (
|
||||||
and !has_narrowing(state, id)) {
|
binding.kind == .declaration and state.cells[usize(binding.data.declaration)].fixed
|
||||||
|
and !has_narrowing(state, id)
|
||||||
|
) {
|
||||||
seed(state, id, state.cells[usize(binding.data.declaration)].type)
|
seed(state, id, state.cells[usize(binding.data.declaration)].type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3187,8 +3269,10 @@ intern_record proc(state @mut State, declaration ast.NodeId, fields []mut Record
|
|||||||
for fields |field, j| {
|
for fields |field, j| {
|
||||||
slot := j
|
slot := j
|
||||||
if (field.name) |name| slot = named_field(state.tree, existing.fields, name)
|
if (field.name) |name| slot = named_field(state.tree, existing.fields, name)
|
||||||
if (slot == no_index or (field.name == null and existing.fields[slot].name != null)
|
if (
|
||||||
or !same_type(state, field.type, existing.fields[slot].type)) {
|
slot == no_index or (field.name == null and existing.fields[slot].name != null)
|
||||||
|
or !same_type(state, field.type, existing.fields[slot].type)
|
||||||
|
) {
|
||||||
equal = false
|
equal = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -3480,8 +3564,10 @@ check_type_expression proc(state @mut State, id ast.NodeId, nominal bool) Type !
|
|||||||
and (operand_binding.data.builtin == .int or operand_binding.data.builtin == .float)
|
and (operand_binding.data.builtin == .int or operand_binding.data.builtin == .float)
|
||||||
)
|
)
|
||||||
if (kind == .distinct_type and type.kind != .invalid) {
|
if (kind == .distinct_type and type.kind != .invalid) {
|
||||||
if (type.kind == .none or type.kind == .void or type.kind == .range or type.kind == .type_value
|
if (
|
||||||
or type.kind == .function or has_inferred_count(state, type) or constraint) {
|
type.kind == .none or type.kind == .void or type.kind == .range or type.kind == .type_value
|
||||||
|
or type.kind == .function or has_inferred_count(state, type) or constraint
|
||||||
|
) {
|
||||||
type = invalid_type(try diagnostics.add(state.diagnostics, .invalid_distinct_backing, ast.node_span(state.tree, id)))
|
type = invalid_type(try diagnostics.add(state.diagnostics, .invalid_distinct_backing, ast.node_span(state.tree, id)))
|
||||||
} else {
|
} else {
|
||||||
state.backings[usize(id)] = type
|
state.backings[usize(id)] = type
|
||||||
@@ -3570,13 +3656,18 @@ check_enum proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError | diagno
|
|||||||
} else valid = false
|
} else valid = false
|
||||||
}
|
}
|
||||||
if (!integer_fits(value, backing.kind)) valid = false
|
if (!integer_fits(value, backing.kind)) valid = false
|
||||||
if (explicit and i != 0 and constant_order(
|
if (
|
||||||
|
explicit and i != 0
|
||||||
|
and constant_order(
|
||||||
ConstantInteger{ magnitude = members[i - 1].magnitude, negative = members[i - 1].negative },
|
ConstantInteger{ magnitude = members[i - 1].magnitude, negative = members[i - 1].negative },
|
||||||
value,
|
value,
|
||||||
) >= 0) valid = false
|
) >= 0
|
||||||
|
) valid = false
|
||||||
for members[0..i] |member| {
|
for members[0..i] |member| {
|
||||||
if (ast.name_id(state.tree, member.name) == ast.name_id(state.tree, field.name?)
|
if (
|
||||||
or (member.magnitude == value.magnitude and member.negative == value.negative)) valid = false
|
ast.name_id(state.tree, member.name) == ast.name_id(state.tree, field.name?)
|
||||||
|
or (member.magnitude == value.magnitude and member.negative == value.negative)
|
||||||
|
) valid = false
|
||||||
}
|
}
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
failure :: invalid_type(try diagnostics.add(state.diagnostics, .invalid_enum_value, field.span))
|
failure :: invalid_type(try diagnostics.add(state.diagnostics, .invalid_enum_value, field.span))
|
||||||
@@ -3648,8 +3739,10 @@ check_union proc(state @mut State, id ast.NodeId, nominal bool) Type ! (mem.Allo
|
|||||||
field :: ast.field(state.tree, ast.record_field(state.tree, id, i))
|
field :: ast.field(state.tree, ast.record_field(state.tree, id, i))
|
||||||
type :: try check_annotation(state, field.type?, true)
|
type :: try check_annotation(state, field.type?, true)
|
||||||
if (type.kind == .invalid) return type
|
if (type.kind == .invalid) return type
|
||||||
if (type.kind == .none or type.kind == .range or type.kind == .type_value or field.value != null
|
if (
|
||||||
or (type.kind == .void and tag.kind == .none and !view.auto_tag)) {
|
type.kind == .none or type.kind == .range or type.kind == .type_value or field.value != null
|
||||||
|
or (type.kind == .void and tag.kind == .none and !view.auto_tag)
|
||||||
|
) {
|
||||||
return invalid_type(try diagnostics.add(state.diagnostics, .invalid_record_field_type, field.span))
|
return invalid_type(try diagnostics.add(state.diagnostics, .invalid_record_field_type, field.span))
|
||||||
}
|
}
|
||||||
if (field.name == null or named_field(state.tree, fields[0..i], field.name?) != no_index) {
|
if (field.name == null or named_field(state.tree, fields[0..i], field.name?) != no_index) {
|
||||||
@@ -3896,8 +3989,10 @@ infer_match proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
|||||||
)
|
)
|
||||||
if (tagged) key = descriptor.tag_type
|
if (tagged) key = descriptor.tag_type
|
||||||
}
|
}
|
||||||
if (subject.kind != .none and !numeric_kind(subject.kind) and subject.kind != .bool
|
if (
|
||||||
and !(subject.kind == .enumeration and subject.nominal == ast.NodeId(maxval!(u32))) and !tagged) {
|
subject.kind != .none and !numeric_kind(subject.kind) and subject.kind != .bool
|
||||||
|
and !(subject.kind == .enumeration and subject.nominal == ast.NodeId(maxval!(u32))) and !tagged
|
||||||
|
) {
|
||||||
try fail(state, id, .invalid_match_subject, ast.node_span(state.tree, id))
|
try fail(state, id, .invalid_match_subject, ast.node_span(state.tree, id))
|
||||||
seed(state, id, state.types[usize(id)])
|
seed(state, id, state.types[usize(id)])
|
||||||
return state.types[usize(id)]
|
return state.types[usize(id)]
|
||||||
@@ -3942,8 +4037,10 @@ infer_match proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.All
|
|||||||
for 0..(state.cells.len) |j| {
|
for 0..(state.cells.len) |j| {
|
||||||
if (state.cells[j].role != .expression or !declaration_reference(state.tree, ast.NodeId(j))) continue
|
if (state.cells[j].role != .expression or !declaration_reference(state.tree, ast.NodeId(j))) continue
|
||||||
binding :: sema.binding(state.resolution, ast.NodeId(j))
|
binding :: sema.binding(state.resolution, ast.NodeId(j))
|
||||||
if (binding.kind == .declaration and binding.data.declaration == capture
|
if (
|
||||||
and !has_narrowing(state, ast.NodeId(j))) seed(state, ast.NodeId(j), payload)
|
binding.kind == .declaration and binding.data.declaration == capture
|
||||||
|
and !has_narrowing(state, ast.NodeId(j))
|
||||||
|
) seed(state, ast.NodeId(j), payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4103,10 +4200,20 @@ validate_match proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | di
|
|||||||
} else try compatible(state, pattern, key)
|
} else try compatible(state, pattern, key)
|
||||||
current :: try pattern_interval(state, pattern, key)
|
current :: try pattern_interval(state, pattern, key)
|
||||||
if (current) |interval| {
|
if (current) |interval| {
|
||||||
if ((interval.real and (interval.real_low > interval.real_high
|
if (
|
||||||
or (!interval.inclusive and interval.real_low == interval.real_high)))
|
(
|
||||||
or (!interval.real and (constant_order(interval.low, interval.high) > 0
|
interval.real and (
|
||||||
or (!interval.inclusive and constant_order(interval.low, interval.high) == 0)))) {
|
interval.real_low > interval.real_high
|
||||||
|
or (!interval.inclusive and interval.real_low == interval.real_high)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or (
|
||||||
|
!interval.real and (
|
||||||
|
constant_order(interval.low, interval.high) > 0
|
||||||
|
or (!interval.inclusive and constant_order(interval.low, interval.high) == 0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
try fail(state, id, .invalid_match_pattern, ast.node_span(state.tree, pattern))
|
try fail(state, id, .invalid_match_pattern, ast.node_span(state.tree, pattern))
|
||||||
}
|
}
|
||||||
if (key.kind == .bool) {
|
if (key.kind == .bool) {
|
||||||
@@ -4145,8 +4252,10 @@ validate_match proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | di
|
|||||||
@hide:file
|
@hide:file
|
||||||
global_variant proc(state @mut State, name ast.NodeId, payload Type) u64 ! mem.AllocError {
|
global_variant proc(state @mut State, name ast.NodeId, payload Type) u64 ! mem.AllocError {
|
||||||
for state.variants.items |variant, i| {
|
for state.variants.items |variant, i| {
|
||||||
if (ast.name_id(state.tree, variant.name) == ast.name_id(state.tree, name)
|
if (
|
||||||
and same_type(state, variant.payload, payload)) return u64(i + 1)
|
ast.name_id(state.tree, variant.name) == ast.name_id(state.tree, name)
|
||||||
|
and same_type(state, variant.payload, payload)
|
||||||
|
) return u64(i + 1)
|
||||||
}
|
}
|
||||||
try arraylist.append(&state.variants, Variant{ name = name, payload = payload })
|
try arraylist.append(&state.variants, Variant{ name = name, payload = payload })
|
||||||
return u64(state.variants.items.len)
|
return u64(state.variants.items.len)
|
||||||
@@ -4181,8 +4290,10 @@ error_subset proc(records []RecordType, enums []EnumType, actual, expected Type)
|
|||||||
if (!descriptor.is_union or descriptor.tag_type.kind != .enumeration) return false
|
if (!descriptor.is_union or descriptor.tag_type.kind != .enumeration) return false
|
||||||
if (descriptor.composed and actual.kind == .record and !records[usize(actual.record_type)].is_union) {
|
if (descriptor.composed and actual.kind == .record and !records[usize(actual.record_type)].is_union) {
|
||||||
for descriptor.fields |field| {
|
for descriptor.fields |field| {
|
||||||
if (field.type.kind == .record and field.type.record_type == actual.record_type
|
if (
|
||||||
and field.type.nominal == actual.nominal) return true
|
field.type.kind == .record and field.type.record_type == actual.record_type
|
||||||
|
and field.type.nominal == actual.nominal
|
||||||
|
) return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -4199,8 +4310,10 @@ error_subset proc(records []RecordType, enums []EnumType, actual, expected Type)
|
|||||||
if (source.kind != .enumeration) return false
|
if (source.kind != .enumeration) return false
|
||||||
origin :: enums[usize(source.enum_type)]
|
origin :: enums[usize(source.enum_type)]
|
||||||
if (origin.explicit_backing or destination.explicit_backing) {
|
if (origin.explicit_backing or destination.explicit_backing) {
|
||||||
if (!origin.explicit_backing or !destination.explicit_backing
|
if (
|
||||||
or origin.declaration != destination.declaration or origin.backing != destination.backing) return false
|
!origin.explicit_backing or !destination.explicit_backing
|
||||||
|
or origin.declaration != destination.declaration or origin.backing != destination.backing
|
||||||
|
) return false
|
||||||
}
|
}
|
||||||
for origin.members |member| {
|
for origin.members |member| {
|
||||||
found := false
|
found := false
|
||||||
@@ -4233,11 +4346,20 @@ check_fallible_type proc(state @mut State, id ast.NodeId) Type ! (mem.AllocError
|
|||||||
failure :: try check_annotation(state, expression.right, false)
|
failure :: try check_annotation(state, expression.right, false)
|
||||||
if (success.kind == .invalid) return success
|
if (success.kind == .invalid) return success
|
||||||
if (failure.kind == .invalid) return failure
|
if (failure.kind == .invalid) return failure
|
||||||
valid_error :: failure.kind == .enumeration or (failure.kind == .record
|
valid_error :: (
|
||||||
and (!state.records.items[usize(failure.record_type)].is_union
|
failure.kind == .enumeration
|
||||||
or state.records.items[usize(failure.record_type)].tag_type.kind == .enumeration))
|
or (
|
||||||
if (!valid_error or success.kind == .none or success.kind == .range or success.kind == .type_value
|
failure.kind == .record
|
||||||
or success.kind == .anyopaque or has_inferred_count(state, success)) {
|
and (
|
||||||
|
!state.records.items[usize(failure.record_type)].is_union
|
||||||
|
or state.records.items[usize(failure.record_type)].tag_type.kind == .enumeration
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
!valid_error or success.kind == .none or success.kind == .range or success.kind == .type_value
|
||||||
|
or success.kind == .anyopaque or has_inferred_count(state, success)
|
||||||
|
) {
|
||||||
return invalid_type(try diagnostics.add(state.diagnostics, .invalid_fallible_type, ast.node_span(state.tree, id)))
|
return invalid_type(try diagnostics.add(state.diagnostics, .invalid_fallible_type, ast.node_span(state.tree, id)))
|
||||||
}
|
}
|
||||||
type :: try intern_array(state, ArrayType{ kind = .fallible, element = success, error_type = failure })
|
type :: try intern_array(state, ArrayType{ kind = .fallible, element = success, error_type = failure })
|
||||||
@@ -4275,8 +4397,10 @@ append_sum_variants proc(state @mut State, variants @mut arraylist.ArrayList(Sum
|
|||||||
name := ast.NodeId(maxval!(u32))
|
name := ast.NodeId(maxval!(u32))
|
||||||
for 0..ast.package_decl_count(state.tree) |i| {
|
for 0..ast.package_decl_count(state.tree) |i| {
|
||||||
declaration :: ast.package_decl(state.tree, i)
|
declaration :: ast.package_decl(state.tree, i)
|
||||||
if (ast.decl_kind(state.tree, declaration) == .statement
|
if (
|
||||||
and ast.statement_decl(state.tree, declaration).initializer == descriptor.declaration) name = declaration
|
ast.decl_kind(state.tree, declaration) == .statement
|
||||||
|
and ast.statement_decl(state.tree, declaration).initializer == descriptor.declaration
|
||||||
|
) name = declaration
|
||||||
}
|
}
|
||||||
if (name == ast.NodeId(maxval!(u32))) {
|
if (name == ast.NodeId(maxval!(u32))) {
|
||||||
for 0..(state.cells.len) |i| {
|
for 0..(state.cells.len) |i| {
|
||||||
@@ -4362,8 +4486,10 @@ seed_capture proc(state @mut State, capture ast.NodeId, type Type) void {
|
|||||||
for 0..(state.cells.len) |i| {
|
for 0..(state.cells.len) |i| {
|
||||||
if (state.cells[i].role != .expression or !declaration_reference(state.tree, ast.NodeId(i))) continue
|
if (state.cells[i].role != .expression or !declaration_reference(state.tree, ast.NodeId(i))) continue
|
||||||
binding :: sema.binding(state.resolution, ast.NodeId(i))
|
binding :: sema.binding(state.resolution, ast.NodeId(i))
|
||||||
if (binding.kind == .declaration and binding.data.declaration == capture
|
if (
|
||||||
and !has_narrowing(state, ast.NodeId(i))) seed(state, ast.NodeId(i), type)
|
binding.kind == .declaration and binding.data.declaration == capture
|
||||||
|
and !has_narrowing(state, ast.NodeId(i))
|
||||||
|
) seed(state, ast.NodeId(i), type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4434,8 +4560,14 @@ validate_fallible_expression proc(state @mut State, id ast.NodeId) void ! (mem.A
|
|||||||
if (result.kind == .invalid) return
|
if (result.kind == .invalid) return
|
||||||
if (result.kind != .fallible) {
|
if (result.kind != .fallible) {
|
||||||
try fail(state, id, .try_outside_fallible, ast.node_span(state.tree, id))
|
try fail(state, id, .try_outside_fallible, ast.node_span(state.tree, id))
|
||||||
} else if (!error_subset(state.records.items, state.enums.items, descriptor.error_type,
|
} else if (
|
||||||
state.arrays.items[usize(result.array_type)].error_type)) {
|
!error_subset(
|
||||||
|
state.records.items,
|
||||||
|
state.enums.items,
|
||||||
|
descriptor.error_type,
|
||||||
|
state.arrays.items[usize(result.array_type)].error_type,
|
||||||
|
)
|
||||||
|
) {
|
||||||
try fail(state, id, .incompatible_propagated_error, ast.node_span(state.tree, id))
|
try fail(state, id, .incompatible_propagated_error, ast.node_span(state.tree, id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4445,8 +4577,10 @@ matching_narrowing_arm proc(state @State, id ast.NodeId) ast.NodeId {
|
|||||||
binding :: sema.binding(state.resolution, id)
|
binding :: sema.binding(state.resolution, id)
|
||||||
if (binding.kind != .declaration) return ast.NodeId(maxval!(u32))
|
if (binding.kind != .declaration) return ast.NodeId(maxval!(u32))
|
||||||
target :: binding.data.declaration
|
target :: binding.data.declaration
|
||||||
if (state.cells[usize(target)].role == .declaration
|
if (
|
||||||
and ast.statement_decl(state.tree, target).mutability == .variable) return ast.NodeId(maxval!(u32))
|
state.cells[usize(target)].role == .declaration
|
||||||
|
and ast.statement_decl(state.tree, target).mutability == .variable
|
||||||
|
) return ast.NodeId(maxval!(u32))
|
||||||
arm := state.cells[usize(id)].narrowing_arm
|
arm := state.cells[usize(id)].narrowing_arm
|
||||||
while (arm != ast.NodeId(maxval!(u32))) {
|
while (arm != ast.NodeId(maxval!(u32))) {
|
||||||
owner :: state.cells[usize(arm)].match_owner
|
owner :: state.cells[usize(arm)].match_owner
|
||||||
@@ -4467,8 +4601,10 @@ has_narrowing proc(state @State, id ast.NodeId) bool {
|
|||||||
arm_names_variant proc(state @State, arm, name ast.NodeId) bool {
|
arm_names_variant proc(state @State, arm, name ast.NodeId) bool {
|
||||||
for 0..ast.match_pattern_count(state.tree, arm) |i| {
|
for 0..ast.match_pattern_count(state.tree, arm) |i| {
|
||||||
pattern :: ast.match_pattern(state.tree, arm, i)
|
pattern :: ast.match_pattern(state.tree, arm, i)
|
||||||
if (ast.expr_kind(state.tree, pattern) == .enum_literal
|
if (
|
||||||
and ast.name_id(state.tree, ast.enum_literal(state.tree, pattern)) == ast.name_id(state.tree, name)) return true
|
ast.expr_kind(state.tree, pattern) == .enum_literal
|
||||||
|
and ast.name_id(state.tree, ast.enum_literal(state.tree, pattern)) == ast.name_id(state.tree, name)
|
||||||
|
) return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,14 +54,17 @@ Instance :: struct {
|
|||||||
@hide:file
|
@hide:file
|
||||||
Rewrite :: struct { target ast.NodeId, saved ast.NodeId }
|
Rewrite :: struct { target ast.NodeId, saved ast.NodeId }
|
||||||
|
|
||||||
#! owns an undo log and retained checking obligations; generated nodes belong to the caller's AST arena.
|
#! owns an undo log and retained checking obligations; deinit releases them with their retained allocator.
|
||||||
|
#! borrows the caller's AST builder; call deinit before finishing or deinitializing that builder.
|
||||||
|
#! generated nodes remain arena-owned and are released with the AST.
|
||||||
Result :: struct {
|
Result :: struct {
|
||||||
builder @mut ast.Builder
|
builder @mut ast.Builder
|
||||||
changes arraylist.ArrayList(Rewrite)
|
changes arraylist.ArrayList(Rewrite)
|
||||||
constraints arraylist.ArrayList(ast.NodeId)
|
constraints arraylist.ArrayList(ast.NodeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
#! restores source nodes so the same compilation may be compiled again.
|
#! restores rewritten source nodes in the borrowed builder and releases the undo log and checking obligations.
|
||||||
|
#! does not remove generated arena nodes or restore resolution bindings.
|
||||||
deinit proc(result @mut Result) void {
|
deinit proc(result @mut Result) void {
|
||||||
index := result.changes.items.len
|
index := result.changes.items.len
|
||||||
while (index > 0) {
|
while (index > 0) {
|
||||||
@@ -94,8 +97,11 @@ State :: struct {
|
|||||||
type_execution_remaining usize = 0
|
type_execution_remaining usize = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#! elaborates templates into cached concrete declarations in the caller-owned arena.
|
#! elaborates templates into cached concrete declarations in the caller-owned `builder` arena.
|
||||||
#! resolution is extended to cover generated nodes; no template reaches checking or lowering.
|
#! extends `resolution` to cover generated nodes; no template reaches checking or lowering.
|
||||||
|
#! `allocator` supplies returned logs, temporary storage released on every exit, and replacement resolution bindings.
|
||||||
|
#! use the allocator retained by `resolution`; the returned Result borrows `builder` until the caller calls deinit.
|
||||||
|
#! on failure, restores rewritten source nodes and releases logs; generated nodes remain arena-owned.
|
||||||
elaborate proc(
|
elaborate proc(
|
||||||
allocator mem.Allocator,
|
allocator mem.Allocator,
|
||||||
builder @mut ast.Builder,
|
builder @mut ast.Builder,
|
||||||
@@ -105,7 +111,7 @@ elaborate proc(
|
|||||||
) Result ! Error {
|
) Result ! Error {
|
||||||
result := Result{ builder = builder, changes = arraylist.init(allocator), constraints = arraylist.init(allocator) }
|
result := Result{ builder = builder, changes = arraylist.init(allocator), constraints = arraylist.init(allocator) }
|
||||||
errdefer deinit(&result)
|
errdefer deinit(&result)
|
||||||
# Explicit evaluation is useful even in packages without generic declarations.
|
# explicit evaluation is useful even in packages without generic declarations.
|
||||||
original :: ast.view(builder, root)
|
original :: ast.view(builder, root)
|
||||||
needed := false
|
needed := false
|
||||||
for (0..ast.node_count(&original)) |i| {
|
for (0..ast.node_count(&original)) |i| {
|
||||||
@@ -250,9 +256,11 @@ expand proc(state @mut State, id ast.NodeId) void ! Error {
|
|||||||
callee :: ast.call_expr(&state.tree, id).callee
|
callee :: ast.call_expr(&state.tree, id).callee
|
||||||
try expand(state, callee)
|
try expand(state, callee)
|
||||||
resolved :: binding(state, callee)
|
resolved :: binding(state, callee)
|
||||||
if (resolved.kind == .declaration
|
if (
|
||||||
|
resolved.kind == .declaration
|
||||||
and ast.node_class(&state.tree, resolved.data.declaration) == .function
|
and ast.node_class(&state.tree, resolved.data.declaration) == .function
|
||||||
and template(state, resolved.data.declaration)) {
|
and template(state, resolved.data.declaration)
|
||||||
|
) {
|
||||||
try specialize_call(state, id, resolved.data.declaration)
|
try specialize_call(state, id, resolved.data.declaration)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -289,7 +297,7 @@ expand proc(state @mut State, id ast.NodeId) void ! Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Only probe side-effect-free expressions. Ordinary calls stay runtime unless the
|
# only probe side-effect-free expressions. ordinary calls stay runtime unless the
|
||||||
# source requests evaluation with $(); probing must not execute FFI or emit traps.
|
# source requests evaluation with $(); probing must not execute FFI or emit traps.
|
||||||
@hide:file
|
@hide:file
|
||||||
branch_constant proc(state @State, id ast.NodeId, depth usize) bool {
|
branch_constant proc(state @State, id ast.NodeId, depth usize) bool {
|
||||||
@@ -362,7 +370,7 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
|||||||
state.depth += 1
|
state.depth += 1
|
||||||
defer state.depth -= 1
|
defer state.depth -= 1
|
||||||
count :: ast.function_param_count(&state.tree, function)
|
count :: ast.function_param_count(&state.tree, function)
|
||||||
# A type result has no runtime call: every input participates in specialization.
|
# a type result has no runtime call: every input participates in specialization.
|
||||||
type_result :: is_type(state, ast.function_decl(&state.tree, function).return_type)
|
type_result :: is_type(state, ast.function_decl(&state.tree, function).return_type)
|
||||||
runtime := usize(0)
|
runtime := usize(0)
|
||||||
for (0..count) |i| {
|
for (0..count) |i| {
|
||||||
@@ -551,13 +559,15 @@ instantiate proc(state @mut State, index usize, call ast.NodeId) void ! Error {
|
|||||||
}
|
}
|
||||||
for (0..count) |n| {
|
for (0..count) |n| {
|
||||||
resolved :: binding(state, ast.NodeId(n))
|
resolved :: binding(state, ast.NodeId(n))
|
||||||
if (resolved.kind == .declaration and resolved.data.declaration == parameter
|
if (
|
||||||
and ast.node_class(&state.tree, ast.NodeId(n)) == .expression) mapping[n] = replacement
|
resolved.kind == .declaration and resolved.data.declaration == parameter
|
||||||
|
and ast.node_class(&state.tree, ast.NodeId(n)) == .expression
|
||||||
|
) mapping[n] = replacement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clone :: try ast.clone_subtree(state.builder, function, mapping)
|
clone :: try ast.clone_subtree(state.builder, function, mapping)
|
||||||
try refresh(state)
|
try refresh(state)
|
||||||
# Copy original resolution, remapping references to cloned locals and runtime parameters.
|
# copy original resolution, remapping references to cloned locals and runtime parameters.
|
||||||
for (0..count) |i| {
|
for (0..count) |i| {
|
||||||
target :: mapping[i]
|
target :: mapping[i]
|
||||||
if (target == no_node or usize(target) < clone_start) continue
|
if (target == no_node or usize(target) < clone_start) continue
|
||||||
@@ -655,7 +665,7 @@ reject_evaluation proc(state @mut State, call ast.NodeId) void ! Error {
|
|||||||
state.bindings.items[usize(call)] = sema.Binding{ kind = .invalid, data = sema.BindingData{ diagnostic = diagnostic } }
|
state.bindings.items[usize(call)] = sema.Binding{ kind = .invalid, data = sema.BindingData{ diagnostic = diagnostic } }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Failed deferred elaboration must not leave unchecked type expressions in the package.
|
# failed deferred elaboration must not leave unchecked type expressions in the package.
|
||||||
@hide:file
|
@hide:file
|
||||||
reject_type_result proc(state @mut State, call, result ast.NodeId, constraints_start usize) void ! Error {
|
reject_type_result proc(state @mut State, call, result ast.NodeId, constraints_start usize) void ! Error {
|
||||||
try reject_evaluation(state, call)
|
try reject_evaluation(state, call)
|
||||||
@@ -714,8 +724,10 @@ argument_annotation proc(
|
|||||||
value :: try materialize(state, arguments[i])
|
value :: try materialize(state, arguments[i])
|
||||||
for (0..mapping.len) |n| {
|
for (0..mapping.len) |n| {
|
||||||
resolved :: binding(state, ast.NodeId(n))
|
resolved :: binding(state, ast.NodeId(n))
|
||||||
if (resolved.kind == .declaration and resolved.data.declaration == parameter
|
if (
|
||||||
and ast.node_class(&state.tree, ast.NodeId(n)) == .expression) mapping[n] = value.node
|
resolved.kind == .declaration and resolved.data.declaration == parameter
|
||||||
|
and ast.node_class(&state.tree, ast.NodeId(n)) == .expression
|
||||||
|
) mapping[n] = value.node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
start :: ast.node_count(&state.tree)
|
start :: ast.node_count(&state.tree)
|
||||||
@@ -738,8 +750,8 @@ evaluate_expected proc(state @mut State, id, annotation ast.NodeId) Value ! Erro
|
|||||||
return try evaluate(state, id)
|
return try evaluate(state, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Values leaving the temporary interpreter must own no interpreter addresses or module IDs.
|
# values leaving the temporary interpreter must own no interpreter addresses or module IDs.
|
||||||
# Rebuild contextual source literals while the module and its escaped flat cells are alive.
|
# rebuild contextual source literals while the module and its escaped flat cells are alive.
|
||||||
@hide:file
|
@hide:file
|
||||||
serialize_value proc(
|
serialize_value proc(
|
||||||
state @mut State, module @ir.Module, checked @typecheck.Result,
|
state @mut State, module @ir.Module, checked @typecheck.Result,
|
||||||
@@ -759,7 +771,7 @@ serialize_value proc(
|
|||||||
result = try ast.add_array_literal(state.builder, span, elements.items)
|
result = try ast.add_array_literal(state.builder, span, elements.items)
|
||||||
} else if (type.kind == .record) {
|
} else if (type.kind == .record) {
|
||||||
record :: typecheck.record_type(checked, type.record_type)
|
record :: typecheck.record_type(checked, type.record_type)
|
||||||
# Untagged storage has no active member; choosing one could discard bytes.
|
# untagged storage has no active member; choosing one could discard bytes.
|
||||||
if (record.is_union and record.tag_type.kind != .enumeration) return no_node
|
if (record.is_union and record.tag_type.kind != .enumeration) return no_node
|
||||||
fields := arraylist.init(ast.NodeId, state.allocator)
|
fields := arraylist.init(ast.NodeId, state.allocator)
|
||||||
defer arraylist.deinit(&fields)
|
defer arraylist.deinit(&fields)
|
||||||
@@ -786,7 +798,7 @@ serialize_value proc(
|
|||||||
child :: ir.aggregate_item(module, value, index)
|
child :: ir.aggregate_item(module, value, index)
|
||||||
result = try serialize_value(state, module, checked, channel, child, span)
|
result = try serialize_value(state, module, checked, channel, child, span)
|
||||||
if (result == no_node) return no_node
|
if (result == no_node) return no_node
|
||||||
# Keep named success/error types distinct even when their members overlap.
|
# keep named success/error types distinct even when their members overlap.
|
||||||
annotation := no_node
|
annotation := no_node
|
||||||
if (channel.nominal != no_node) annotation = channel.nominal
|
if (channel.nominal != no_node) annotation = channel.nominal
|
||||||
else if (channel.kind == .enumeration) annotation = typecheck.enum_type(checked, channel.enum_type).declaration
|
else if (channel.kind == .enumeration) annotation = typecheck.enum_type(checked, channel.enum_type).declaration
|
||||||
@@ -927,7 +939,7 @@ evaluate_call proc(state @mut State, call ast.NodeId) Value ! Error {
|
|||||||
}
|
}
|
||||||
try arraylist.append(&arguments, concrete.node)
|
try arraylist.append(&arguments, concrete.node)
|
||||||
}
|
}
|
||||||
# Reuse checking, typed IR and the interpreter: compile-time arithmetic and local
|
# reuse checking, typed IR and the interpreter: compile-time arithmetic and local
|
||||||
# inference must have exactly the same semantics as an ordinary runtime call.
|
# inference must have exactly the same semantics as an ordinary runtime call.
|
||||||
span :: ast.node_span(&state.tree, call)
|
span :: ast.node_span(&state.tree, call)
|
||||||
invocation :: try ast.add_call(state.builder, span, callee.node, arguments.items)
|
invocation :: try ast.add_call(state.builder, span, callee.node, arguments.items)
|
||||||
@@ -1022,7 +1034,7 @@ TypeExecution :: struct {
|
|||||||
@hide:file
|
@hide:file
|
||||||
TypeFlow :: enum { next, returned, break, continue, invalid }
|
TypeFlow :: enum { next, returned, break, continue, invalid }
|
||||||
|
|
||||||
# Type values cannot enter runtime IR. Execute their control flow here, substituting
|
# type values cannot enter runtime IR. execute their control flow here, substituting
|
||||||
# owned constants into snapshots; ordinary value operations still use checked IR.
|
# owned constants into snapshots; ordinary value operations still use checked IR.
|
||||||
@hide:file
|
@hide:file
|
||||||
return_type_value proc(state @mut State, block, first_owned ast.NodeId) ast.NodeId ! Error {
|
return_type_value proc(state @mut State, block, first_owned ast.NodeId) ast.NodeId ! Error {
|
||||||
@@ -1054,7 +1066,7 @@ type_step proc(state @mut State, at ast.NodeId) bool ! Error {
|
|||||||
@hide:file
|
@hide:file
|
||||||
prepare_type_snapshot proc(state @mut State, execution @mut TypeExecution, id ast.NodeId) void {
|
prepare_type_snapshot proc(state @mut State, execution @mut TypeExecution, id ast.NodeId) void {
|
||||||
if (usize(id) >= execution.mapping.len) return
|
if (usize(id) >= execution.mapping.len) return
|
||||||
# Substituted arguments belong to the caller, including nominal type identity.
|
# substituted arguments belong to the caller, including nominal type identity.
|
||||||
if (id < execution.first_owned) {
|
if (id < execution.first_owned) {
|
||||||
execution.mapping[usize(id)] = id
|
execution.mapping[usize(id)] = id
|
||||||
return
|
return
|
||||||
@@ -1105,7 +1117,7 @@ nominal_type_value proc(state @State, id ast.NodeId) bool {
|
|||||||
return kind == .record_type or kind == .union_type or kind == .enum_type or kind == .distinct_type
|
return kind == .record_type or kind == .union_type or kind == .enum_type or kind == .distinct_type
|
||||||
}
|
}
|
||||||
|
|
||||||
# Select with ordinary checked IR so patterns, overlap and exhaustiveness have
|
# select with ordinary checked IR so patterns, overlap and exhaustiveness have
|
||||||
# the same rules as runtime matching, without checking unexecuted type bodies.
|
# the same rules as runtime matching, without checking unexecuted type bodies.
|
||||||
@hide:file
|
@hide:file
|
||||||
type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
||||||
@@ -1117,7 +1129,7 @@ type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
|||||||
for (0..ast.match_arm_count(&state.tree, id)) |i| {
|
for (0..ast.match_arm_count(&state.tree, id)) |i| {
|
||||||
arm_id :: ast.match_arm(&state.tree, id, i)
|
arm_id :: ast.match_arm(&state.tree, id, i)
|
||||||
arm :: ast.match_arm_view(&state.tree, arm_id)
|
arm :: ast.match_arm_view(&state.tree, arm_id)
|
||||||
# Selector bodies omit source capture uses, but must retain their resolver failures.
|
# selector bodies omit source capture uses, but must retain their resolver failures.
|
||||||
if (arm.capture) |capture| {
|
if (arm.capture) |capture| {
|
||||||
resolved :: binding(state, capture)
|
resolved :: binding(state, capture)
|
||||||
if (resolved.kind == .invalid) {
|
if (resolved.kind == .invalid) {
|
||||||
@@ -1125,8 +1137,13 @@ type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
|||||||
return no_index
|
return no_index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i > 0 and arm.expression != ast.match_arm_view(&state.tree,
|
if (
|
||||||
ast.match_arm(&state.tree, id, 0)).expression) {
|
i > 0
|
||||||
|
and arm.expression != ast.match_arm_view(
|
||||||
|
&state.tree,
|
||||||
|
ast.match_arm(&state.tree, id, 0),
|
||||||
|
).expression
|
||||||
|
) {
|
||||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
.type_mismatch, ast.node_span(&state.tree, arm_id))
|
.type_mismatch, ast.node_span(&state.tree, arm_id))
|
||||||
return no_index
|
return no_index
|
||||||
@@ -1152,7 +1169,7 @@ type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
|||||||
return usize(selected.magnitude)
|
return usize(selected.magnitude)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Match checking has already validated every capture. Copy the active payload
|
# match checking has already validated every capture. copy the active payload
|
||||||
# out of owned constant storage, never an address in the temporary interpreter.
|
# out of owned constant storage, never an address in the temporary interpreter.
|
||||||
@hide:file
|
@hide:file
|
||||||
type_match_capture proc(state @mut State, id ast.NodeId) ast.NodeId ! Error {
|
type_match_capture proc(state @mut State, id ast.NodeId) ast.NodeId ! Error {
|
||||||
@@ -1163,8 +1180,10 @@ type_match_capture proc(state @mut State, id ast.NodeId) ast.NodeId ! Error {
|
|||||||
payload :: ast.field(&state.tree, ast.record_field(&state.tree, value.node, 0))
|
payload :: ast.field(&state.tree, ast.record_field(&state.tree, value.node, 0))
|
||||||
for (0..ast.record_field_count(&state.tree, annotation)) |i| {
|
for (0..ast.record_field_count(&state.tree, annotation)) |i| {
|
||||||
field :: ast.field(&state.tree, ast.record_field(&state.tree, annotation, i))
|
field :: ast.field(&state.tree, ast.record_field(&state.tree, annotation, i))
|
||||||
if (field.name != null and payload.name != null
|
if (
|
||||||
and ast.name_id(&state.tree, field.name?) == ast.name_id(&state.tree, payload.name?)) {
|
field.name != null and payload.name != null
|
||||||
|
and ast.name_id(&state.tree, field.name?) == ast.name_id(&state.tree, payload.name?)
|
||||||
|
) {
|
||||||
result :: try ast.add_typed_constant(state.builder,
|
result :: try ast.add_typed_constant(state.builder,
|
||||||
ast.node_span(&state.tree, subject), field.type?, payload.value?)
|
ast.node_span(&state.tree, subject), field.type?, payload.value?)
|
||||||
try refresh(state)
|
try refresh(state)
|
||||||
@@ -1208,8 +1227,8 @@ type_value_expression proc(state @mut State, input ast.NodeId) ast.NodeId ! Erro
|
|||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
|
|
||||||
# Normalize only selected expression arms before ordinary expansion can visit them.
|
# normalize only selected expression arms before ordinary expansion can visit them.
|
||||||
# Nominal results enter here after their instance declaration is cached.
|
# nominal results enter here after their instance declaration is cached.
|
||||||
@hide:file
|
@hide:file
|
||||||
expand_type_matches proc(state @mut State, id ast.NodeId) bool ! Error {
|
expand_type_matches proc(state @mut State, id ast.NodeId) bool ! Error {
|
||||||
if (ast.node_class(&state.tree, id) == .expression and ast.expr_kind(&state.tree, id) == .match_expr) {
|
if (ast.node_class(&state.tree, id) == .expression and ast.expr_kind(&state.tree, id) == .match_expr) {
|
||||||
@@ -1233,7 +1252,7 @@ type_execution_value proc(state @mut State, input, annotation ast.NodeId) ast.No
|
|||||||
expression :: try type_value_expression(state, input)
|
expression :: try type_value_expression(state, input)
|
||||||
if (expression == no_node) return no_node
|
if (expression == no_node) return no_node
|
||||||
if (!try expand_type_matches(state, annotation)) return no_node
|
if (!try expand_type_matches(state, annotation)) return no_node
|
||||||
# A recursive record needs its cached declaration before expanding its fields.
|
# a recursive record needs its cached declaration before expanding its fields.
|
||||||
if (is_type(state, annotation) and nominal_type_value(state, expression)) {
|
if (is_type(state, annotation) and nominal_type_value(state, expression)) {
|
||||||
concrete :: try materialize(state, Value{ kind = .type, node = expression })
|
concrete :: try materialize(state, Value{ kind = .type, node = expression })
|
||||||
constraint :: try ast.add_type_operand(state.builder, ast.node_span(&state.tree, expression), concrete.node, true)
|
constraint :: try ast.add_type_operand(state.builder, ast.node_span(&state.tree, expression), concrete.node, true)
|
||||||
@@ -1327,9 +1346,11 @@ execute_type_statement proc(state @mut State, execution @mut TypeExecution, stat
|
|||||||
if (local.declaration == resolved.data.declaration) index = i
|
if (local.declaration == resolved.data.declaration) index = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (index == no_index
|
if (
|
||||||
|
index == no_index
|
||||||
or ast.node_class(&state.tree, execution.locals.items[index].declaration) != .statement
|
or ast.node_class(&state.tree, execution.locals.items[index].declaration) != .statement
|
||||||
or ast.statement_decl(&state.tree, execution.locals.items[index].declaration).mutability != .variable) {
|
or ast.statement_decl(&state.tree, execution.locals.items[index].declaration).mutability != .variable
|
||||||
|
) {
|
||||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
.invalid_assignment_target, ast.node_span(&state.tree, assignment.target))
|
.invalid_assignment_target, ast.node_span(&state.tree, assignment.target))
|
||||||
return .invalid
|
return .invalid
|
||||||
@@ -1572,9 +1593,11 @@ evaluate_binary proc(state @mut State, id ast.NodeId) Value ! Error {
|
|||||||
result_type :: try builtin(state, id, kind)
|
result_type :: try builtin(state, id, kind)
|
||||||
return try evaluate_ir(state, id, result_type)
|
return try evaluate_ir(state, id, result_type)
|
||||||
}
|
}
|
||||||
if (left.annotation != no_node and right.annotation != no_node
|
if (
|
||||||
|
left.annotation != no_node and right.annotation != no_node
|
||||||
and left.kind == .integer and right.kind == .integer
|
and left.kind == .integer and right.kind == .integer
|
||||||
and !try same_type(state, left.annotation, right.annotation)) return Value{}
|
and !try same_type(state, left.annotation, right.annotation)
|
||||||
|
) return Value{}
|
||||||
if (view.operator == .equal or view.operator == .not_equal) {
|
if (view.operator == .equal or view.operator == .not_equal) {
|
||||||
if (left.kind != right.kind) return Value{}
|
if (left.kind != right.kind) return Value{}
|
||||||
equal :: try value_equal(state, left, right)
|
equal :: try value_equal(state, left, right)
|
||||||
@@ -1790,8 +1813,8 @@ type_hash proc(state @mut State, id ast.NodeId) usize ! Error {
|
|||||||
.optional_type: return mix(hash, try type_hash(state, ast.type_operand(&state.tree, node)))
|
.optional_type: return mix(hash, try type_hash(state, ast.type_operand(&state.tree, node)))
|
||||||
.sum_type: {
|
.sum_type: {
|
||||||
view :: ast.binary_expr(&state.tree, node)
|
view :: ast.binary_expr(&state.tree, node)
|
||||||
# Set fingerprints must ignore ordering, grouping and repeated members.
|
# set fingerprints must ignore ordering, grouping and repeated members.
|
||||||
# Colliding fingerprints still use full type equality in the cache chain.
|
# colliding fingerprints still use full type equality in the cache chain.
|
||||||
return try type_hash(state, view.left) | try type_hash(state, view.right)
|
return try type_hash(state, view.left) | try type_hash(state, view.right)
|
||||||
}
|
}
|
||||||
.fallible_type: {
|
.fallible_type: {
|
||||||
@@ -1914,9 +1937,11 @@ expression_type proc(state @mut State, id ast.NodeId, depth usize) ast.NodeId !
|
|||||||
}
|
}
|
||||||
.binary: {
|
.binary: {
|
||||||
view :: ast.binary_expr(&state.tree, id)
|
view :: ast.binary_expr(&state.tree, id)
|
||||||
if (view.operator == .equal or view.operator == .not_equal or view.operator == .less
|
if (
|
||||||
|
view.operator == .equal or view.operator == .not_equal or view.operator == .less
|
||||||
or view.operator == .less_equal or view.operator == .greater or view.operator == .greater_equal
|
or view.operator == .less_equal or view.operator == .greater or view.operator == .greater_equal
|
||||||
or view.operator == .logical_and or view.operator == .logical_or) return try builtin(state, id, .bool)
|
or view.operator == .logical_and or view.operator == .logical_or
|
||||||
|
) return try builtin(state, id, .bool)
|
||||||
return try expression_type(state, view.left, depth + 1)
|
return try expression_type(state, view.left, depth + 1)
|
||||||
}
|
}
|
||||||
.selection: {
|
.selection: {
|
||||||
@@ -1927,8 +1952,12 @@ expression_type proc(state @mut State, id ast.NodeId, depth usize) ast.NodeId !
|
|||||||
if (base == no_node) return no_node
|
if (base == no_node) return no_node
|
||||||
member :: ast.source_text(&state.tree, ast.node_span(&state.tree, view.member))
|
member :: ast.source_text(&state.tree, ast.node_span(&state.tree, view.member))
|
||||||
base_kind :: ast.expr_kind(&state.tree, base)
|
base_kind :: ast.expr_kind(&state.tree, base)
|
||||||
if (mem.eql(member, "len") and (base_kind == .array_type or base_kind == .slice_type
|
if (
|
||||||
or (binding(state, base).kind == .builtin and binding(state, base).data.builtin == .string)))
|
mem.eql(member, "len") and (
|
||||||
|
base_kind == .array_type or base_kind == .slice_type
|
||||||
|
or (binding(state, base).kind == .builtin and binding(state, base).data.builtin == .string)
|
||||||
|
)
|
||||||
|
)
|
||||||
return try builtin(state, id, .usize)
|
return try builtin(state, id, .usize)
|
||||||
if (base_kind != .record_type and base_kind != .union_type) return no_node
|
if (base_kind != .record_type and base_kind != .union_type) return no_node
|
||||||
for (0..ast.record_field_count(&state.tree, base)) |i| {
|
for (0..ast.record_field_count(&state.tree, base)) |i| {
|
||||||
@@ -2062,11 +2091,13 @@ materialize proc(state @mut State, value Value) Value ! Error {
|
|||||||
result := value
|
result := value
|
||||||
span :: ast.node_span(&state.tree, value.node)
|
span :: ast.node_span(&state.tree, value.node)
|
||||||
if (value.kind == .type and nominal_type_value(state, value.node)) {
|
if (value.kind == .type and nominal_type_value(state, value.node)) {
|
||||||
# Refer to declared nominal types, rather than reusing their definition as
|
# refer to declared nominal types, rather than reusing their definition as
|
||||||
# an annotation and turning its value-role checker cell into an annotation.
|
# an annotation and turning its value-role checker cell into an annotation.
|
||||||
for (state.declarations.items) |declaration| {
|
for (state.declarations.items) |declaration| {
|
||||||
if (ast.node_class(&state.tree, declaration) == .statement
|
if (
|
||||||
and ast.statement_decl(&state.tree, declaration).initializer == value.node) {
|
ast.node_class(&state.tree, declaration) == .statement
|
||||||
|
and ast.statement_decl(&state.tree, declaration).initializer == value.node
|
||||||
|
) {
|
||||||
result.node = try reference(state, value.node, declaration)
|
result.node = try reference(state, value.node, declaration)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-5
@@ -20,17 +20,21 @@ Entry :: struct {
|
|||||||
kind Kind
|
kind Kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#! returns caller-owned zero-terminated storage; release it with mem.free using the same allocator.
|
||||||
@hide:file
|
@hide:file
|
||||||
terminated proc(allocator mem.Allocator, text []u8) []mut u8 ! Error {
|
terminated proc(allocator mem.Allocator, text []u8) []mut u8 ! Error {
|
||||||
if (text.len == 0 or text.len == maxval!(usize)) return .invalid_path
|
if (text.len == 0 or text.len == maxval!(usize)) return .invalid_path
|
||||||
for (text) |byte| if (byte == 0) return .invalid_path
|
for (text) |byte| {
|
||||||
|
if (byte == 0) return .invalid_path
|
||||||
|
}
|
||||||
bytes :: try mem.alloc(u8, allocator, text.len + 1)
|
bytes :: try mem.alloc(u8, allocator, text.len + 1)
|
||||||
memcopy!(bytes[..text.len], text)
|
memcopy!(bytes[..text.len], text)
|
||||||
bytes[text.len] = 0
|
bytes[text.len] = 0
|
||||||
return bytes
|
return bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
#! returns an owned absolute path with symlinks resolved; free it with the same allocator.
|
#! returns an owned absolute path with symlinks resolved; release it with mem.free using the same allocator.
|
||||||
|
#! temporary path storage uses `allocator` and is released before returning.
|
||||||
canonical proc(allocator mem.Allocator, path []u8) []mut u8 ! Error {
|
canonical proc(allocator mem.Allocator, path []u8) []mut u8 ! Error {
|
||||||
input :: try terminated(allocator, path)
|
input :: try terminated(allocator, path)
|
||||||
defer mem.free(allocator, input)
|
defer mem.free(allocator, input)
|
||||||
@@ -49,6 +53,7 @@ canonical proc(allocator mem.Allocator, path []u8) []mut u8 ! Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#! follows symlinks; nonexistent paths (including dangling links) return missing.
|
#! follows symlinks; nonexistent paths (including dangling links) return missing.
|
||||||
|
#! temporary path storage uses `allocator` and is released before returning.
|
||||||
kind proc(allocator mem.Allocator, path []u8) Kind ! Error {
|
kind proc(allocator mem.Allocator, path []u8) Kind ! Error {
|
||||||
input :: try terminated(allocator, path)
|
input :: try terminated(allocator, path)
|
||||||
defer mem.free(allocator, input)
|
defer mem.free(allocator, input)
|
||||||
@@ -69,7 +74,9 @@ exists proc(allocator mem.Allocator, path []u8) bool ! Error {
|
|||||||
return (try kind(allocator, path)) != .missing
|
return (try kind(allocator, path)) != .missing
|
||||||
}
|
}
|
||||||
|
|
||||||
#! opens an owned directory handle; path is borrowed only during this call.
|
#! opens an owned directory handle; `path` is borrowed only during this call.
|
||||||
|
#! temporary path storage uses `allocator` and is released before returning.
|
||||||
|
#! release the returned handle with close_directory.
|
||||||
open_directory proc(allocator mem.Allocator, path []u8) Directory ! Error {
|
open_directory proc(allocator mem.Allocator, path []u8) Directory ! Error {
|
||||||
input :: try terminated(allocator, path)
|
input :: try terminated(allocator, path)
|
||||||
defer mem.free(allocator, input)
|
defer mem.free(allocator, input)
|
||||||
@@ -119,9 +126,10 @@ close_directory proc(directory @mut Directory) void ! Error {
|
|||||||
if (c.closedir(handle) != 0) return .directory_failed
|
if (c.closedir(handle) != 0) return .directory_failed
|
||||||
}
|
}
|
||||||
|
|
||||||
#! reads a regular file through runtime IO, accepting at most maximum bytes.
|
#! reads a regular file through runtime IO, accepting at most `maximum` bytes.
|
||||||
#! follows symlinks and rejects devices/FIFOs before opening; metadata uses the host filesystem.
|
#! follows symlinks and rejects devices/FIFOs before opening; metadata uses the host filesystem.
|
||||||
#! returned storage belongs to the caller; release with arraylist.deinit. all errors close the file.
|
#! returned storage belongs to the caller and retains `allocator`; release with arraylist.deinit. all errors close the file.
|
||||||
|
#! temporary path storage uses `allocator` and is released before returning.
|
||||||
read_file proc(
|
read_file proc(
|
||||||
allocator mem.Allocator,
|
allocator mem.Allocator,
|
||||||
runtime io.Io,
|
runtime io.Io,
|
||||||
|
|||||||
+5
-2
@@ -26,7 +26,8 @@ raw_alloc proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#! resize caller-owned storage with its original allocator and alignment.
|
#! resize caller-owned storage with its original allocator and alignment.
|
||||||
#! on allocation failure with nonzero `new_size`, original storage remains caller-owned; release the result with raw_free.
|
#! on allocation failure with nonzero `new_size`, original storage remains caller-owned.
|
||||||
|
#! release returned storage with raw_free using the same allocator and alignment, with `new_size` as its size.
|
||||||
raw_realloc proc(
|
raw_realloc proc(
|
||||||
allocator Allocator,
|
allocator Allocator,
|
||||||
memory ?*mut u8,
|
memory ?*mut u8,
|
||||||
@@ -49,7 +50,9 @@ raw_free proc(
|
|||||||
|
|
||||||
eql proc($T type, left, right []T) bool {
|
eql proc($T type, left, right []T) bool {
|
||||||
if (left.len != right.len) return false
|
if (left.len != right.len) return false
|
||||||
for (0..left.len) |i| if (left[i] != right[i]) return false
|
for (0..left.len) |i| {
|
||||||
|
if (left[i] != right[i]) return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ RunError :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#! run a program synchronously without a shell, searching PATH for `arguments[0]`.
|
#! run a program synchronously without a shell, searching PATH for `arguments[0]`.
|
||||||
#! arguments and zero-terminated strings are borrowed until this call returns.
|
#! `arguments` and zero-terminated strings are borrowed until this call returns.
|
||||||
#! an empty argument vector, empty executable name, or embedded NUL is invalid.
|
#! an empty argument vector, empty executable name, or embedded NUL is invalid.
|
||||||
#! the child inherits the current environment and stdin/stdout/stderr.
|
#! the child inherits the current environment and stdin/stdout/stderr.
|
||||||
#! temporary argument descriptors use `allocator` and are freed on every return.
|
#! temporary argument descriptors use `allocator` and are freed on every return.
|
||||||
@@ -28,7 +28,9 @@ run proc(allocator mem.Allocator, arguments [][;0]u8) i32 ! (mem.AllocError | Ru
|
|||||||
if (arguments.len == 0) return .invalid_arguments
|
if (arguments.len == 0) return .invalid_arguments
|
||||||
if (arguments[0].len == 0) return .invalid_arguments
|
if (arguments[0].len == 0) return .invalid_arguments
|
||||||
for arguments |argument| {
|
for arguments |argument| {
|
||||||
for (argument) |byte| if (byte == 0) return .invalid_arguments
|
for (argument) |byte| {
|
||||||
|
if (byte == 0) return .invalid_arguments
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (arguments.len == maxval!(usize)) return .out_of_memory
|
if (arguments.len == maxval!(usize)) return .out_of_memory
|
||||||
|
|
||||||
@@ -61,6 +63,6 @@ run proc(allocator mem.Allocator, arguments [][;0]u8) i32 ! (mem.AllocError | Ru
|
|||||||
signal :: status[0] & 127
|
signal :: status[0] & 127
|
||||||
if (signal == 0) return i32(divtrunc!(status[0], 256) & 255)
|
if (signal == 0) return i32(divtrunc!(status[0], 256) & 255)
|
||||||
if (signal != 127) return i32(128 + signal)
|
if (signal != 127) return i32(128 + signal)
|
||||||
# A traced child can report a stop even without WUNTRACED; wait for termination.
|
# a traced child can report a stop even without WUNTRACED; wait for termination.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ parse_float proc(allocator mem.Allocator, text []u8) f64 ! (mem.AllocError | Par
|
|||||||
magnitude >= u64(9218868437227405312)
|
magnitude >= u64(9218868437227405312)
|
||||||
or (magnitude == 0 and nonzero)
|
or (magnitude == 0 and nonzero)
|
||||||
or (range_error and magnitude >= u64(4607182418800017408))
|
or (range_error and magnitude >= u64(4607182418800017408))
|
||||||
) return .out_of_range
|
) {
|
||||||
|
return .out_of_range
|
||||||
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,11 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
|
|||||||
compile_error!("static string map key is too long")
|
compile_error!("static string map key is too long")
|
||||||
}
|
}
|
||||||
|
|
||||||
for (0..i) |prior| if mem.eql(u8, entry.0, entries[prior].0) {
|
for (0..i) |prior| {
|
||||||
|
if mem.eql(u8, entry.0, entries[prior].0) {
|
||||||
compile_error!("duplicate static string map key")
|
compile_error!("duplicate static string map key")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
keys[i] = entry.0
|
keys[i] = entry.0
|
||||||
values[i] = entry.1
|
values[i] = entry.1
|
||||||
|
|||||||
Reference in New Issue
Block a user