feat: support existing io.print formatting
This commit is contained in:
+24
-8
@@ -521,6 +521,7 @@ lower_function proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | di
|
||||
owned :: try mem.alloc(u8, state.module.allocator, name.len)
|
||||
memcopy!(owned, name)
|
||||
state.module.functions.items[state.function].external_name = owned
|
||||
state.module.functions.items[state.function].variadic = ast.callable_variadic(tree, id)
|
||||
return
|
||||
}
|
||||
try predeclare_locals(state, declaration.body)
|
||||
@@ -1657,9 +1658,11 @@ convert_value proc(state @mut State, value ir.ValueId, actual, expected typechec
|
||||
if (actual.kind == .optional and expected.kind == .optional and actual.array_type != expected.array_type) {
|
||||
source :: typecheck.array_type(state.checked, actual.array_type).element
|
||||
child :: typecheck.array_type(state.checked, expected.array_type).element
|
||||
if (source.kind == .pointer or source.kind == .function) return value
|
||||
nullable :: child.kind == .pointer or child.kind == .function
|
||||
if ((source.kind == .pointer or source.kind == .function) and nullable) return value
|
||||
runtime :: lower_type(state, expected)
|
||||
shape :: try array_shape(state, expected)
|
||||
slot :: u32(try ir.add_local(state.module, ir.FunctionId(state.function), .record, shape))
|
||||
slot :: u32(try ir.add_local(state.module, ir.FunctionId(state.function), runtime, shape))
|
||||
present :: try optional_present(state, value, actual, span)
|
||||
some :: try add_block(state)
|
||||
none :: try add_block(state)
|
||||
@@ -1670,25 +1673,33 @@ convert_value proc(state @mut State, value ir.ValueId, actual, expected typechec
|
||||
state.block = some
|
||||
payload :: try optional_payload(state, value, actual, span)
|
||||
converted :: try convert_value(state, payload, source, child, span)
|
||||
wrapped :: try record_pair(state, shape, present, converted, span)
|
||||
wrapped := converted
|
||||
if (!nullable) wrapped = try record_pair(state, shape, present, converted, span)
|
||||
_ = try emit(state, ir.Instruction{ op = .store_local, left = wrapped, slot = slot, span = span })
|
||||
state.module.functions.items[state.function].blocks.items[usize(state.block)].terminator = ir.Terminator{
|
||||
kind = .jump, target = join, span = span,
|
||||
}
|
||||
state.block = none
|
||||
absent :: try zero_value(state, .record, shape, span)
|
||||
absent :: try zero_value(state, runtime, shape, span)
|
||||
_ = try emit(state, ir.Instruction{ op = .store_local, left = absent, slot = slot, span = span })
|
||||
state.module.functions.items[state.function].blocks.items[usize(state.block)].terminator = ir.Terminator{
|
||||
kind = .jump, target = join, span = span,
|
||||
}
|
||||
state.block = join
|
||||
return try emit(state, ir.Instruction{ op = .load_local, type = .record, array_type = shape, slot = slot, span = span })
|
||||
return try emit(state, ir.Instruction{ op = .load_local, type = runtime, array_type = shape, slot = slot, span = span })
|
||||
}
|
||||
if (expected.kind == .pointer and actual.kind == .string)
|
||||
return try emit(state, ir.Instruction{ op = .string_pointer, type = .pointer, left = value, span = span })
|
||||
if (expected.kind == .slice and actual.kind == .string) {
|
||||
pointer :: try emit(state, ir.Instruction{ op = .string_pointer, type = .pointer, left = value, span = span })
|
||||
length :: try emit(state, ir.Instruction{ op = .string_length, type = .usize, left = value, span = span })
|
||||
return try record_pair(state, try array_shape(state, expected), pointer, length, span)
|
||||
}
|
||||
if (expected.kind == .slice and actual.kind == .pointer) {
|
||||
pointer :: typecheck.pointer_type(state.checked, actual.pointer_type)
|
||||
length :: try usize_constant(state, typecheck.array_type(state.checked, pointer.element.array_type).len, span)
|
||||
return try record_pair(state, try array_shape(state, expected), value, length, span)
|
||||
}
|
||||
if (expected.kind == .slice and actual.kind == .array) {
|
||||
source_shape :: try array_shape(state, actual)
|
||||
slot :: u32(try ir.add_local(state.module, ir.FunctionId(state.function), .array, source_shape))
|
||||
@@ -1975,13 +1986,15 @@ lower_builtin proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError
|
||||
else value.data = ir.ValueData{ integer = enummap.get(&ir.integer_minima, type)? }
|
||||
return try constant(state, value, span)
|
||||
}
|
||||
if (mem.eql(name, "divtrunc")) {
|
||||
if (mem.eql(name, "divtrunc") or mem.eql(name, "rem")) {
|
||||
left :: try lower_expression(state, ast.call_arg(tree, id, 0))
|
||||
if (state.terminated) return ir.no_value
|
||||
right :: try lower_expression(state, ast.call_arg(tree, id, 1))
|
||||
if (state.terminated) return ir.no_value
|
||||
op ir.Op :: if (mem.eql(name, "rem")) .remainder else .divide
|
||||
return try emit(state, ir.Instruction{
|
||||
op = .divide, type = lower_type(state, typecheck.type_of(state.checked, id)),
|
||||
op = op,
|
||||
type = lower_type(state, typecheck.type_of(state.checked, id)),
|
||||
left = left, right = right, span = span,
|
||||
})
|
||||
}
|
||||
@@ -2471,8 +2484,9 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
||||
count :: ast.call_arg_count(tree, id)
|
||||
try arraylist.reserve(&state.arguments, scratch_start + count)
|
||||
for (0..count) |_| try arraylist.append(&state.arguments, ir.no_value)
|
||||
signature :: typecheck.type_of(state.checked, ast.call_expr(tree, id).callee).function
|
||||
fixed_arguments :: if (signature != ast.NodeId(maxval!(u32))) ast.function_param_count(tree, signature) else 0
|
||||
for (0..count) |i| {
|
||||
signature :: typecheck.type_of(state.checked, ast.call_expr(tree, id).callee).function
|
||||
expected := typecheck.Type{}
|
||||
if (signature != ast.NodeId(maxval!(u32)) and i < ast.function_param_count(tree, signature)) expected = typecheck.type_of(state.checked, ast.param(tree, ast.function_param(tree, signature, i)).type)
|
||||
argument :: try lower_as(state, ast.call_arg(tree, id, i), expected)
|
||||
@@ -2492,6 +2506,8 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
||||
left = callee,
|
||||
arguments_start = start,
|
||||
arguments_count = count,
|
||||
variadic = signature != ast.NodeId(maxval!(u32)) and ast.callable_variadic(tree, signature),
|
||||
fixed_arguments = fixed_arguments,
|
||||
span = span,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2049,13 +2049,10 @@ keeps_generated_foreign_declarations_demand_driven_and_traps_interpreted_calls t
|
||||
try expect_trap(foreign_outcome, &diagnostic_store, .unsupported_foreign_call)
|
||||
}
|
||||
|
||||
diagnoses_demanded_unsupported_foreign_signatures_and_attributes test {
|
||||
sources [5][]u8 :: [
|
||||
"take c_func(_ c_int, ...) c_int\nmain proc() c_int { return take(1) }\n",
|
||||
diagnoses_demanded_opaque_values_and_incompatible_opaque_pointers test {
|
||||
sources [2][]u8 :: [
|
||||
"Handle :: opaque\ntake c_func(_ Handle) void\nmain proc() void { take(undefined) }\n",
|
||||
"A :: opaque\nB :: opaque\nmain proc() void { a ?*A :: null\n b ?*B :: a }\n",
|
||||
"@other:file\nhidden :: 1\nmain proc() int { return hidden }\n",
|
||||
"@hide:package\nhidden :: 1\nmain proc() int { return hidden }\n",
|
||||
]
|
||||
for sources |source| {
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
@@ -2087,3 +2084,88 @@ rejects_demanded_unavailable_c_floating_types_without_unknown_names test {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preserves_variadic_call_signatures_and_interpreter_foreign_traps test {
|
||||
sources [2][]u8 :: [
|
||||
"take c_func(_ c_int, ...) c_int\nmain proc() c_int { return take(1) }\n",
|
||||
"take c_func(_ c_int, ...) c_int\nmain proc() c_int { return take(1, true, i8(-2), u8(3), i16(-4), u16(5), 6.5) }\n",
|
||||
]
|
||||
for sources |source| {
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
module := try compile_owned(source, &diagnostic_store)
|
||||
defer ir.deinit(&module)
|
||||
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
|
||||
try testing.expect(module.functions.items[0].variadic)
|
||||
try testing.expect_equal(usize(1), module.functions.items[0].parameters.items.len)
|
||||
entry :: &module.functions.items[usize(module.entry?)]
|
||||
found := false
|
||||
for entry.blocks.items |block| {
|
||||
for block.instructions.items |instruction| {
|
||||
if (instruction.op != .call) continue
|
||||
found = true
|
||||
try testing.expect(instruction.variadic)
|
||||
try testing.expect_equal(usize(1), instruction.fixed_arguments)
|
||||
}
|
||||
}
|
||||
try testing.expect(found)
|
||||
outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store)
|
||||
defer interpreter.deinit(&outcome)
|
||||
try expect_trap(outcome, &diagnostic_store, .unsupported_foreign_call)
|
||||
}
|
||||
}
|
||||
|
||||
rejects_variadic_arity_fixed_types_and_non_c_trailing_values test {
|
||||
sources [4][]u8 :: [
|
||||
"take c_func(_ c_int, ...) c_int\nmain proc() c_int { return take() }\n",
|
||||
"take c_func(_ c_int, ...) c_int\nmain proc() c_int { return take(true) }\n",
|
||||
"take c_func(_ c_int, ...) c_int\nmain proc() c_int { return take(1, \"text\") }\n",
|
||||
"take c_func(_ c_int, ...) c_int\nmain proc() c_int { values [2]i64 :: [1, 2]\nreturn take(1, values) }\n",
|
||||
]
|
||||
codes [4]diagnostics.Code :: [
|
||||
.argument_count_mismatch, .type_mismatch, .invalid_operand, .invalid_operand,
|
||||
]
|
||||
for sources |source, index| {
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
module := try compile_owned(source, &diagnostic_store)
|
||||
defer ir.deinit(&module)
|
||||
outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store)
|
||||
defer interpreter.deinit(&outcome)
|
||||
try expect_trap(outcome, &diagnostic_store, codes[index])
|
||||
}
|
||||
}
|
||||
|
||||
coerces_strings_to_immutable_byte_pointer_arguments test {
|
||||
sources [3][]u8 :: [
|
||||
"take c_func(_ ?*c_char) void\nmain proc() void { take(\"format\") }\n",
|
||||
"take c_func(_ *u8) void\nmain proc() void { take(\"format\") }\n",
|
||||
"take c_func(_ *i8, ...) void\nmain proc() void { take(\"format\", true) }\n",
|
||||
]
|
||||
for sources |source| {
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
module := try compile_owned(source, &diagnostic_store)
|
||||
defer ir.deinit(&module)
|
||||
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
|
||||
outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store)
|
||||
defer interpreter.deinit(&outcome)
|
||||
try expect_trap(outcome, &diagnostic_store, .unsupported_foreign_call)
|
||||
}
|
||||
}
|
||||
|
||||
rejects_string_arguments_for_mutable_or_nonbyte_pointers test {
|
||||
sources [2][]u8 :: [
|
||||
"take c_func(_ ?*mut c_char) void\nmain proc() void { take(\"format\") }\n",
|
||||
"take c_func(_ *u16) void\nmain proc() void { take(\"format\") }\n",
|
||||
]
|
||||
for sources |source| {
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
module := try compile_owned(source, &diagnostic_store)
|
||||
defer ir.deinit(&module)
|
||||
outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store)
|
||||
defer interpreter.deinit(&outcome)
|
||||
try expect_trap(outcome, &diagnostic_store, .type_mismatch)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user