diff options
Diffstat (limited to 'src/wasm-ast-checker.c')
-rw-r--r-- | src/wasm-ast-checker.c | 769 |
1 files changed, 441 insertions, 328 deletions
diff --git a/src/wasm-ast-checker.c b/src/wasm-ast-checker.c index b940d621..2d0ca5b6 100644 --- a/src/wasm-ast-checker.c +++ b/src/wasm-ast-checker.c @@ -28,91 +28,71 @@ #include "wasm-binary-reader-ast.h" #include "wasm-binary-reader.h" -static const char* s_type_names[] = { - "void", "i32", "i64", "f32", "f64", -}; -WASM_STATIC_ASSERT(WASM_ARRAY_SIZE(s_type_names) == WASM_NUM_TYPES); - -typedef enum TypeSet { - WASM_TYPE_SET_VOID = 0, - WASM_TYPE_SET_I32 = 1 << 0, - WASM_TYPE_SET_I64 = 1 << 1, - WASM_TYPE_SET_F32 = 1 << 2, - WASM_TYPE_SET_F64 = 1 << 3, - WASM_TYPE_SET_ALL = (1 << 4) - 1, -} TypeSet; - -static const char* s_type_set_names[] = { - "void", - "i32", - "i64", - "i32 or i64", - "f32", - "i32 or f32", - "i64 or f32", - "i32, i64 or f32", - "f64", - "i32 or f64", - "i64 or f64", - "i32, i64 or f64", - "f32 or f64", - "i32, f32 or f64", - "i64, f32 or f64", - "i32, i64, f32 or f64", -}; -WASM_STATIC_ASSERT(WASM_ARRAY_SIZE(s_type_set_names) == WASM_TYPE_SET_ALL + 1); -WASM_STATIC_ASSERT((1 << (WASM_TYPE_I32 - 1)) == WASM_TYPE_SET_I32); -WASM_STATIC_ASSERT((1 << (WASM_TYPE_I64 - 1)) == WASM_TYPE_SET_I64); -WASM_STATIC_ASSERT((1 << (WASM_TYPE_F32 - 1)) == WASM_TYPE_SET_F32); -WASM_STATIC_ASSERT((1 << (WASM_TYPE_F64 - 1)) == WASM_TYPE_SET_F64); - -#define TYPE_TO_TYPE_SET(type) ((type) ? 1 << ((type)-1) : 0) +typedef enum WasmCheckType { + WASM_CHECK_TYPE_VOID = WASM_TYPE_VOID, + WASM_CHECK_TYPE_I32 = WASM_TYPE_I32, + WASM_CHECK_TYPE_I64 = WASM_TYPE_I64, + WASM_CHECK_TYPE_F32 = WASM_TYPE_F32, + WASM_CHECK_TYPE_F64 = WASM_TYPE_F64, + WASM_CHECK_TYPE_ANY = WASM_NUM_TYPES, + WASM_NUM_CHECK_TYPES, + WASM_CHECK_TYPE____ = WASM_CHECK_TYPE_VOID, /* see table in wasm-common.h */ +} WasmCheckType; +WASM_DEFINE_VECTOR(check_type, WasmCheckType); + +static const char* s_type_names[] = {"void", "i32", "i64", "f32", "f64", "any"}; +WASM_STATIC_ASSERT(WASM_ARRAY_SIZE(s_type_names) == WASM_NUM_CHECK_TYPES); #define V(rtype, type1, type2, mem_size, code, NAME, text) \ - [code] = WASM_TYPE_##rtype, -static WasmType s_opcode_rtype[] = {WASM_FOREACH_OPCODE(V)}; + [code] = WASM_CHECK_TYPE_##rtype, +static WasmCheckType s_opcode_rtype[] = {WASM_FOREACH_OPCODE(V)}; #undef V #define V(rtype, type1, type2, mem_size, code, NAME, text) \ - [code] = WASM_TYPE_##type1, -static WasmType s_opcode_type1[] = {WASM_FOREACH_OPCODE(V)}; + [code] = WASM_CHECK_TYPE_##type1, +static WasmCheckType s_opcode_type1[] = {WASM_FOREACH_OPCODE(V)}; #undef V #define V(rtype, type1, type2, mem_size, code, NAME, text) \ - [code] = WASM_TYPE_##type2, -static WasmType s_opcode_type2[] = {WASM_FOREACH_OPCODE(V)}; + [code] = WASM_CHECK_TYPE_##type2, +static WasmCheckType s_opcode_type2[] = {WASM_FOREACH_OPCODE(V)}; #undef V -typedef enum CheckType { - CHECK_TYPE_NAME, - CHECK_TYPE_FULL, -} CheckType; +typedef size_t WasmSizeT; +WASM_DEFINE_VECTOR(size_t, WasmSizeT); + +typedef enum CheckStyle { + CHECK_STYLE_NAME, + CHECK_STYLE_FULL, +} CheckStyle; typedef struct LabelNode { const WasmLabel* label; - WasmType expected_type; + WasmCheckType type; const char* desc; struct LabelNode* next; } LabelNode; typedef struct Context { - CheckType check_type; + CheckStyle check_style; WasmSourceErrorHandler* error_handler; WasmAllocator* allocator; WasmAstLexer* lexer; const WasmModule* current_module; const WasmFunc* current_func; + WasmSizeTVector type_stack_limit; + WasmCheckTypeVector type_stack; LabelNode* top_label; int max_depth; WasmResult result; } Context; static void WASM_PRINTF_FORMAT(4, 5) print_error(Context* ctx, - CheckType check_type, + CheckStyle check_style, const WasmLocation* loc, const char* fmt, ...) { - if (check_type <= ctx->check_type) { + if (check_style <= ctx->check_style) { ctx->result = WASM_ERROR; va_list args; va_start(args, fmt); @@ -147,7 +127,7 @@ static void check_duplicate_bindings(Context* ctx, WasmLocation* a_loc = &a->binding.loc; WasmLocation* b_loc = &b->binding.loc; WasmLocation* loc = a_loc->line > b_loc->line ? a_loc : b_loc; - print_error(ctx, CHECK_TYPE_NAME, loc, + print_error(ctx, CHECK_STYLE_NAME, loc, "redefinition of %s \"" PRIstringslice "\"", desc, WASM_PRINTF_STRING_SLICE_ARG(a->binding.name)); } @@ -169,11 +149,11 @@ static WasmResult check_var(Context* ctx, return WASM_OK; } if (var->type == WASM_VAR_TYPE_NAME) { - print_error(ctx, CHECK_TYPE_NAME, &var->loc, + print_error(ctx, CHECK_STYLE_NAME, &var->loc, "undefined %s variable \"" PRIstringslice "\"", desc, WASM_PRINTF_STRING_SLICE_ARG(var->name)); } else { - print_error(ctx, CHECK_TYPE_NAME, &var->loc, + print_error(ctx, CHECK_STYLE_NAME, &var->loc, "%s variable out of range (max %d)", desc, max_index); } return WASM_ERROR; @@ -226,7 +206,7 @@ static WasmResult check_func_type_var(Context* ctx, static WasmResult check_local_var(Context* ctx, const WasmVar* var, - WasmType* out_type) { + WasmCheckType* out_type) { const WasmModule* module = ctx->current_module; const WasmFunc* func = ctx->current_func; int max_index = wasm_get_num_params_and_locals(module, func); @@ -244,11 +224,11 @@ static WasmResult check_local_var(Context* ctx, } if (var->type == WASM_VAR_TYPE_NAME) { - print_error(ctx, CHECK_TYPE_NAME, &var->loc, + print_error(ctx, CHECK_STYLE_NAME, &var->loc, "undefined local variable \"" PRIstringslice "\"", WASM_PRINTF_STRING_SLICE_ARG(var->name)); } else { - print_error(ctx, CHECK_TYPE_NAME, &var->loc, + print_error(ctx, CHECK_STYLE_NAME, &var->loc, "local variable out of range (max %d)", max_index); } return WASM_ERROR; @@ -258,68 +238,18 @@ static void check_align(Context* ctx, const WasmLocation* loc, uint32_t alignment) { if (alignment != WASM_USE_NATURAL_ALIGNMENT && !is_power_of_two(alignment)) - print_error(ctx, CHECK_TYPE_FULL, loc, "alignment must be power-of-two"); + print_error(ctx, CHECK_STYLE_FULL, loc, "alignment must be power-of-two"); } static void check_offset(Context* ctx, const WasmLocation* loc, uint64_t offset) { if (offset > UINT32_MAX) { - print_error(ctx, CHECK_TYPE_FULL, loc, + print_error(ctx, CHECK_STYLE_FULL, loc, "offset must be less than or equal to 0xffffffff"); } } -static void check_type(Context* ctx, - const WasmLocation* loc, - WasmType actual, - WasmType expected, - const char* desc) { - if (expected != WASM_TYPE_VOID && actual != expected) { - print_error(ctx, CHECK_TYPE_FULL, loc, - "type mismatch%s. got %s, expected %s", desc, - s_type_names[actual], s_type_names[expected]); - } -} - -static void check_type_set(Context* ctx, - const WasmLocation* loc, - WasmType actual, - TypeSet expected, - const char* desc) { - if (expected != WASM_TYPE_SET_VOID && - (TYPE_TO_TYPE_SET(actual) & expected) == 0) { - print_error(ctx, CHECK_TYPE_FULL, loc, - "type mismatch%s. got %s, expected %s", desc, - s_type_names[actual], s_type_set_names[expected]); - } -} - -static void check_type_exact(Context* ctx, - const WasmLocation* loc, - WasmType actual, - WasmType expected, - const char* desc) { - if (actual != expected) { - print_error(ctx, CHECK_TYPE_FULL, loc, - "type mismatch%s. got %s, expected %s", desc, - s_type_names[actual], s_type_names[expected]); - } -} - -static void check_type_arg_exact(Context* ctx, - const WasmLocation* loc, - WasmType actual, - WasmType expected, - const char* desc, - int arg_index) { - if (actual != expected) { - print_error(ctx, CHECK_TYPE_FULL, loc, - "type mismatch for argument %d of %s. got %s, expected %s", - arg_index, desc, s_type_names[actual], s_type_names[expected]); - } -} - static LabelNode* find_label_by_name(LabelNode* top_label, const WasmStringSlice* name) { LabelNode* node = top_label; @@ -356,11 +286,11 @@ static WasmResult check_label_var(Context* ctx, } if (var->type == WASM_VAR_TYPE_NAME) { - print_error(ctx, CHECK_TYPE_NAME, &var->loc, + print_error(ctx, CHECK_STYLE_NAME, &var->loc, "undefined label variable \"" PRIstringslice "\"", WASM_PRINTF_STRING_SLICE_ARG(var->name)); } else { - print_error(ctx, CHECK_TYPE_NAME, &var->loc, + print_error(ctx, CHECK_STYLE_NAME, &var->loc, "label variable out of range (max %d)", ctx->max_depth); } @@ -371,11 +301,11 @@ static void push_label(Context* ctx, const WasmLocation* loc, LabelNode* node, const WasmLabel* label, - WasmType expected_type, + WasmCheckType type, const char* desc) { node->label = label; node->next = ctx->top_label; - node->expected_type = expected_type; + node->type = type; node->desc = desc; ctx->top_label = node; ctx->max_depth++; @@ -386,131 +316,298 @@ static void pop_label(Context* ctx) { ctx->top_label = ctx->top_label->next; } -static void check_expr(Context* ctx, - const WasmExpr* expr, - WasmType expected_type, - const char* desc); +static size_t type_stack_limit(Context* ctx) { + return ctx->type_stack_limit.data[ctx->type_stack_limit.size - 1]; +} + +static size_t push_type_stack_limit(Context* ctx) { + size_t limit = ctx->type_stack.size; + wasm_append_size_t_value(ctx->allocator, &ctx->type_stack_limit, &limit); + return limit; +} + +static void pop_type_stack_limit(Context* ctx) { + assert(ctx->type_stack_limit.size > 0); + ctx->type_stack_limit.size--; +} + +static void push_type(Context* ctx, WasmCheckType type) { + if (type != WASM_CHECK_TYPE_VOID) + wasm_append_check_type_value(ctx->allocator, &ctx->type_stack, &type); +} + +static WasmCheckType peek_type(Context* ctx, size_t depth, int arity) { + if (arity > 0) { + if (depth < ctx->type_stack.size - type_stack_limit(ctx)) { + return ctx->type_stack.data[ctx->type_stack.size - depth - 1]; + } else { + /* return any type to allow type-checking to continue; the caller should + * have already raised an error about the stack underflow. */ + return WASM_CHECK_TYPE_I32; + } + } else { + return WASM_CHECK_TYPE_VOID; + } +} + +static WasmCheckType top_type(Context* ctx) { + return peek_type(ctx, 0, 1); +} + +static WasmCheckType pop_type(Context* ctx) { + WasmCheckType result = top_type(ctx); + if (ctx->type_stack.size > type_stack_limit(ctx)) + ctx->type_stack.size--; + return result; +} + +static void check_type(Context* ctx, + const WasmLocation* loc, + WasmCheckType actual, + WasmCheckType expected, + const char* desc) { + if (actual != expected) { + print_error(ctx, CHECK_STYLE_FULL, loc, + "type mismatch at %s. got %s, expected %s", desc, + s_type_names[actual], s_type_names[expected]); + } +} -static void check_expr_opt(Context* ctx, +static void check_type_arg(Context* ctx, const WasmLocation* loc, - const WasmExpr* expr, - WasmType expected_type, - const char* desc) { - if (expr) - check_expr(ctx, expr, expected_type, desc); - else - check_type(ctx, loc, WASM_TYPE_VOID, expected_type, desc); + WasmCheckType actual, + WasmCheckType expected, + const char* desc, + int arg_index) { + if (actual != expected) { + print_error(ctx, CHECK_STYLE_FULL, loc, + "type mismatch for argument %d of %s. got %s, expected %s", + arg_index, desc, s_type_names[actual], s_type_names[expected]); + } +} + +static void check_assert_return_nan_type(Context* ctx, + const WasmLocation* loc, + WasmCheckType actual, + const char* desc) { + /* when using assert_return_nan, the result can be either a f32 or f64 type + * so we special case it here. */ + if (actual != WASM_CHECK_TYPE_F32 && actual != WASM_CHECK_TYPE_F64) { + print_error(ctx, CHECK_STYLE_FULL, loc, + "type mismatch at %s. got %s, expected f32 or f64", desc, + s_type_names[actual]); + } +} + +static WasmCheckType join_type(Context* ctx, + const WasmLocation* loc, + WasmCheckType t1, + WasmCheckType t2, + const char* desc) { + if (t1 == WASM_CHECK_TYPE_ANY) { + return t2; + } else if (t2 == WASM_CHECK_TYPE_ANY) { + return t1; + } else if (t1 == t2) { + return t1; + } else { + print_error(ctx, CHECK_STYLE_FULL, loc, + "type mismatch at %s. got %s, expected %s", desc, + s_type_names[t1], s_type_names[t2]); + return t1; + } +} + +static void unify_label_type(Context* ctx, + const WasmLocation* loc, + LabelNode* node, + WasmCheckType type, + const char* desc) { + if (node->type == WASM_CHECK_TYPE_ANY) { + node->type = type; + } else if (type != WASM_CHECK_TYPE_ANY && node->type != type) { + print_error(ctx, CHECK_STYLE_FULL, loc, + "type mismatch at %s. got %s, expected %s", desc, + s_type_names[type], s_type_names[node->type]); + } +} + +static void transform_stack(Context* ctx, + const WasmLocation* loc, + const char* desc, + size_t before_size, + size_t after_size, + ...) { + size_t i; + va_list args; + va_start(args, after_size); + size_t limit = type_stack_limit(ctx); + size_t avail = ctx->type_stack.size - limit; + if (before_size <= avail) { + for (i = 0; i < before_size; ++i) { + WasmCheckType actual = + ctx->type_stack.data[ctx->type_stack.size - before_size + i]; + WasmCheckType expected = va_arg(args, WasmCheckType); + /* TODO(binji): could give a better location for the error by storing the + * location in the type stack; i.e. where this type was added to the + * stack */ + check_type(ctx, loc, actual, expected, desc); + } + ctx->type_stack.size -= before_size; + } else { + print_error(ctx, CHECK_STYLE_FULL, loc, + "type stack size too small at %s. got %" PRIzd + ", expected at least %" PRIzd, + desc, avail, before_size); + ctx->type_stack.size = limit; + } + assert(after_size <= 1); + if (after_size > 0) + push_type(ctx, va_arg(args, WasmCheckType)); + va_end(args); } static void check_br(Context* ctx, const WasmLocation* loc, const WasmVar* var, - const WasmExpr* expr, + WasmCheckType type, const char* desc) { LabelNode* node; - if (WASM_FAILED(check_label_var(ctx, ctx->top_label, var, &node))) - return; - - check_expr_opt(ctx, loc, expr, node->expected_type, desc); + if (WASM_SUCCEEDED(check_label_var(ctx, ctx->top_label, var, &node))) { + unify_label_type(ctx, loc, node, type, desc); + } } static void check_call(Context* ctx, const WasmLocation* loc, const WasmStringSlice* callee_name, const WasmFuncSignature* sig, - const WasmExpr* first_arg, - size_t num_args, - WasmType expected_type, const char* desc) { size_t expected_args = sig->param_types.size; - if (expected_args == num_args) { - char buffer[100]; - wasm_snprintf(buffer, 100, " of %s result", desc); - check_type(ctx, loc, sig->result_type, expected_type, buffer); - const WasmExpr* arg; + size_t limit = type_stack_limit(ctx); + size_t avail = ctx->type_stack.size - limit; + if (expected_args <= avail) { size_t i; - for (i = 0, arg = first_arg; i < num_args; ++i, arg = arg->next) { - wasm_snprintf(buffer, 100, " of argument %" PRIzd " of %s", i, desc); - check_expr(ctx, arg, sig->param_types.data[i], buffer); + for (i = 0; i < sig->param_types.size; ++i) { + WasmCheckType actual = + ctx->type_stack.data[ctx->type_stack.size - expected_args + i]; + WasmCheckType expected = sig->param_types.data[i]; + check_type_arg(ctx, loc, actual, expected, desc, i); } + ctx->type_stack.size -= expected_args; } else { - char* callee_name_str = ""; - if (callee_name && callee_name->start) { - size_t length = callee_name->length + 10; - callee_name_str = alloca(length); - wasm_snprintf(callee_name_str, length, " \"" PRIstringslice "\"", - WASM_PRINTF_STRING_SLICE_ARG(*callee_name)); - } - print_error(ctx, CHECK_TYPE_FULL, loc, - "too %s parameters to function%s in %s. got %" PRIzd - ", expected %" PRIzd, - num_args > expected_args ? "many" : "few", callee_name_str, - desc, num_args, expected_args); + print_error(ctx, CHECK_STYLE_FULL, loc, + "type stack size too small at %s. got %" PRIzd + ", expected at least %" PRIzd, + desc, avail, expected_args); + ctx->type_stack.size = limit; } + push_type(ctx, sig->result_type); } -static void check_expr_list(Context* ctx, - const WasmLocation* loc, - const WasmExpr* first, - WasmType expected_type, - const char* desc) { +static void check_expr(Context* ctx, const WasmExpr* expr); + +static WasmCheckType check_block(Context* ctx, + const WasmLocation* loc, + const WasmExpr* first, + const char* desc) { if (first) { + size_t limit = push_type_stack_limit(ctx); const WasmExpr* expr; for (expr = first; expr; expr = expr->next) { - if (expr->next) - check_expr(ctx, expr, WASM_TYPE_VOID, ""); - else - check_expr(ctx, expr, expected_type, desc); + check_expr(ctx, expr); + /* stop typechecking if we hit unreachable code */ + if (top_type(ctx) == WASM_CHECK_TYPE_ANY) + break; } + WasmCheckType result = top_type(ctx); + if (result != WASM_CHECK_TYPE_ANY) { + size_t result_arity = ctx->type_stack.size - limit; + if (result_arity > 1) { + print_error(ctx, CHECK_STYLE_FULL, loc, + "maximum arity for %s is 1, got %" PRIzd, desc, + result_arity); + } else if (result_arity == 0) { + result = WASM_CHECK_TYPE_VOID; + } + } + ctx->type_stack.size = limit; + pop_type_stack_limit(ctx); + return result; } else { - check_type(ctx, loc, WASM_TYPE_VOID, expected_type, desc); + return WASM_CHECK_TYPE_VOID; } } -static void check_expr(Context* ctx, - const WasmExpr* expr, - WasmType expected_type, - const char* desc) { +static void check_expr(Context* ctx, const WasmExpr* expr) { switch (expr->type) { case WASM_EXPR_TYPE_BINARY: { - WasmType rtype = s_opcode_rtype[expr->binary.opcode]; - WasmType type1 = s_opcode_type1[expr->binary.opcode]; - WasmType type2 = s_opcode_type2[expr->binary.opcode]; - check_type(ctx, &expr->loc, rtype, expected_type, desc); - check_expr(ctx, expr->binary.left, type1, " of argument 0 of binary op"); - check_expr(ctx, expr->binary.right, type2, " of argument 1 of binary op"); + WasmCheckType rtype = s_opcode_rtype[expr->binary.opcode]; + WasmCheckType type1 = s_opcode_type1[expr->binary.opcode]; + WasmCheckType type2 = s_opcode_type2[expr->binary.opcode]; + transform_stack(ctx, &expr->loc, "binary", 2, 1, type1, type2, rtype); break; } case WASM_EXPR_TYPE_BLOCK: { LabelNode node; - push_label(ctx, &expr->loc, &node, &expr->block.label, expected_type, - "block"); - check_expr_list(ctx, &expr->loc, expr->block.first, expected_type, - " of block"); + push_label(ctx, &expr->loc, &node, &expr->block.label, + WASM_CHECK_TYPE_ANY, "block"); + WasmCheckType rtype = + check_block(ctx, &expr->loc, expr->block.first, "block"); + rtype = join_type(ctx, &expr->loc, node.type, rtype, "block"); pop_label(ctx); + push_type(ctx, rtype); break; } case WASM_EXPR_TYPE_BR: { - check_br(ctx, &expr->loc, &expr->br.var, expr->br.expr, " of br value"); + WasmCheckType type = peek_type(ctx, 0, expr->br.arity); + check_br(ctx, &expr->loc, &expr->br.var, type, "br value"); + if (expr->br.arity > 0) { + transform_stack(ctx, &expr->loc, "br", 1, 1, type, WASM_CHECK_TYPE_ANY); + } else { + transform_stack(ctx, &expr->loc, "br", 0, 1, WASM_CHECK_TYPE_ANY); + } break; } case WASM_EXPR_TYPE_BR_IF: { - check_type(ctx, &expr->loc, WASM_TYPE_VOID, expected_type, desc); - check_br(ctx, &expr->loc, &expr->br_if.var, expr->br_if.expr, - " of br_if value"); - check_expr(ctx, expr->br_if.cond, WASM_TYPE_I32, " of br_if condition"); + WasmCheckType type = peek_type(ctx, 1, expr->br_if.arity); + check_br(ctx, &expr->loc, &expr->br_if.var, type, "br_if value"); + if (expr->br_if.arity > 0) { + transform_stack(ctx, &expr->loc, "br_if", 2, 1, type, + WASM_CHECK_TYPE_I32, WASM_CHECK_TYPE_VOID); + } else { + transform_stack(ctx, &expr->loc, "br_if", 1, 1, WASM_CHECK_TYPE_I32, + WASM_CHECK_TYPE_VOID); + } + break; + } + + case WASM_EXPR_TYPE_BR_TABLE: { + WasmCheckType type = peek_type(ctx, 1, expr->br_table.arity); + size_t i; + for (i = 0; i < expr->br_table.targets.size; ++i) { + check_br(ctx, &expr->loc, &expr->br_table.targets.data[i], type, + "br_table target"); + } + check_br(ctx, &expr->loc, &expr->br_table.default_target, type, + "br_table default target"); + if (expr->br_table.arity > 0) { + transform_stack(ctx, &expr->loc, "br_table", 2, 1, type, + WASM_CHECK_TYPE_I32, WASM_CHECK_TYPE_ANY); + } else { + transform_stack(ctx, &expr->loc, "br_table", 1, 1, WASM_CHECK_TYPE_I32, + WASM_CHECK_TYPE_ANY); + } break; } case WASM_EXPR_TYPE_CALL: { const WasmFunc* callee; if (WASM_SUCCEEDED(check_func_var(ctx, &expr->call.var, &callee))) { - check_call(ctx, &expr->loc, &callee->name, &callee->decl.sig, - expr->call.first_arg, expr->call.num_args, expected_type, - "call"); + check_call(ctx, &expr->loc, &callee->name, &callee->decl.sig, "call"); } break; } @@ -519,182 +616,172 @@ static void check_expr(Context* ctx, const WasmImport* import; if (WASM_SUCCEEDED(check_import_var(ctx, &expr->call.var, &import))) { check_call(ctx, &expr->loc, &import->name, &import->decl.sig, - expr->call.first_arg, expr->call.num_args, expected_type, "call_import"); } break; } case WASM_EXPR_TYPE_CALL_INDIRECT: { - check_expr(ctx, expr->call_indirect.expr, WASM_TYPE_I32, - " of function index"); const WasmFuncType* func_type; if (WASM_SUCCEEDED( - check_func_type_var(ctx, &expr->call_indirect.var, &func_type))) { - check_call(ctx, &expr->loc, NULL, &func_type->sig, - expr->call_indirect.first_arg, expr->call_indirect.num_args, - expected_type, "call_indirect"); + check_func_type_var(ctx, &expr->call.var, &func_type))) { + WasmCheckType type = pop_type(ctx); + check_type(ctx, &expr->loc, type, WASM_CHECK_TYPE_I32, + "function index"); + check_call(ctx, &expr->loc, NULL, &func_type->sig, "call_indirect"); } break; } case WASM_EXPR_TYPE_COMPARE: { - WasmType rtype = s_opcode_rtype[expr->compare.opcode]; - WasmType type1 = s_opcode_type1[expr->compare.opcode]; - WasmType type2 = s_opcode_type2[expr->compare.opcode]; - check_type(ctx, &expr->loc, rtype, expected_type, desc); - check_expr(ctx, expr->compare.left, type1, - " of argument 0 of compare op"); - check_expr(ctx, expr->compare.right, type2, - " of argument 1 of compare op"); + WasmCheckType rtype = s_opcode_rtype[expr->compare.opcode]; + WasmCheckType type1 = s_opcode_type1[expr->compare.opcode]; + WasmCheckType type2 = s_opcode_type2[expr->compare.opcode]; + transform_stack(ctx, &expr->loc, "compare", 2, 1, type1, type2, rtype); break; } case WASM_EXPR_TYPE_CONST: - check_type(ctx, &expr->loc, expr->const_.type, expected_type, desc); + push_type(ctx, expr->const_.type); break; case WASM_EXPR_TYPE_CONVERT: { - WasmType rtype = s_opcode_rtype[expr->convert.opcode]; - WasmType type1 = s_opcode_type1[expr->convert.opcode]; - check_type(ctx, &expr->loc, rtype, expected_type, desc); - check_expr(ctx, expr->convert.expr, type1, " of convert op"); + WasmCheckType rtype = s_opcode_rtype[expr->convert.opcode]; + WasmCheckType type1 = s_opcode_type1[expr->convert.opcode]; + transform_stack(ctx, &expr->loc, "convert", 1, 1, type1, rtype); + break; + } + + case WASM_EXPR_TYPE_DROP: { + WasmType type = top_type(ctx); + transform_stack(ctx, &expr->loc, "drop", 1, 0, type); break; } case WASM_EXPR_TYPE_GET_LOCAL: { - WasmType type; + WasmCheckType type; if (WASM_SUCCEEDED(check_local_var(ctx, &expr->get_local.var, &type))) { - check_type(ctx, &expr->loc, type, expected_type, desc); + push_type(ctx, type); } break; } case WASM_EXPR_TYPE_GROW_MEMORY: - check_type(ctx, &expr->loc, WASM_TYPE_I32, expected_type, - " in grow_memory"); - check_expr(ctx, expr->grow_memory.expr, WASM_TYPE_I32, " of grow_memory"); + transform_stack(ctx, &expr->loc, "grow_memory", 1, 1, WASM_CHECK_TYPE_I32, + WASM_CHECK_TYPE_I32); break; case WASM_EXPR_TYPE_IF: { LabelNode node; - check_expr(ctx, expr->if_.cond, WASM_TYPE_I32, " of condition"); - push_label(ctx, &expr->loc, &node, &expr->if_.true_.label, expected_type, - "if branch"); - check_expr_list(ctx, &expr->loc, expr->if_.true_.first, expected_type, - " of if branch"); + push_label(ctx, &expr->loc, &node, &expr->if_.true_.label, + WASM_CHECK_TYPE_ANY, "if branch"); + WasmCheckType rtype = + check_block(ctx, &expr->loc, expr->if_.true_.first, "if expression"); + rtype = join_type(ctx, &expr->loc, rtype, WASM_CHECK_TYPE_VOID, + "if expression"); pop_label(ctx); - check_type(ctx, &expr->loc, WASM_TYPE_VOID, expected_type, desc); + transform_stack(ctx, &expr->loc, "if expression", 1, 1, + WASM_CHECK_TYPE_I32, rtype); break; } case WASM_EXPR_TYPE_IF_ELSE: { LabelNode node; - check_expr(ctx, expr->if_else.cond, WASM_TYPE_I32, " of condition"); push_label(ctx, &expr->loc, &node, &expr->if_else.true_.label, - expected_type, "if true branch"); - check_expr_list(ctx, &expr->loc, expr->if_else.true_.first, expected_type, - " of if branch"); + WASM_CHECK_TYPE_ANY, "if true branch"); + WasmCheckType rtype1 = check_block( + ctx, &expr->loc, expr->if_else.true_.first, "if true branch"); pop_label(ctx); push_label(ctx, &expr->loc, &node, &expr->if_else.false_.label, - expected_type, "if false branch"); - check_expr_list(ctx, &expr->loc, expr->if_else.false_.first, - expected_type, " of if branch"); + WASM_CHECK_TYPE_ANY, "if false branch"); + WasmCheckType rtype2 = check_block( + ctx, &expr->loc, expr->if_else.false_.first, "if false branch"); pop_label(ctx); + WasmCheckType rtype = + join_type(ctx, &expr->loc, rtype1, rtype2, "of if expression"); + transform_stack(ctx, &expr->loc, "if expression", 1, 1, + WASM_CHECK_TYPE_I32, rtype); break; } case WASM_EXPR_TYPE_LOAD: { - WasmType rtype = s_opcode_rtype[expr->load.opcode]; - WasmType type1 = s_opcode_type1[expr->load.opcode]; + WasmCheckType rtype = s_opcode_rtype[expr->load.opcode]; + WasmCheckType type1 = s_opcode_type1[expr->load.opcode]; check_align(ctx, &expr->loc, expr->load.align); check_offset(ctx, &expr->loc, expr->load.offset); - check_type(ctx, &expr->loc, rtype, expected_type, desc); - check_expr(ctx, expr->load.addr, type1, " of load index"); + transform_stack(ctx, &expr->loc, "load", 1, 1, type1, rtype); break; } case WASM_EXPR_TYPE_LOOP: { - LabelNode outer_node; - LabelNode inner_node; - push_label(ctx, &expr->loc, &outer_node, &expr->loop.outer, expected_type, - "loop outer label"); - push_label(ctx, &expr->loc, &inner_node, &expr->loop.inner, - WASM_TYPE_VOID, "loop inner label"); - check_expr_list(ctx, &expr->loc, expr->loop.first, expected_type, - " of loop"); - pop_label(ctx); + LabelNode node; + push_label(ctx, &expr->loc, &node, &expr->loop.label, + WASM_CHECK_TYPE_VOID, "loop label"); + WasmCheckType rtype = + check_block(ctx, &expr->loc, expr->loop.first, "loop"); pop_label(ctx); + push_type(ctx, rtype); break; } case WASM_EXPR_TYPE_CURRENT_MEMORY: - check_type(ctx, &expr->loc, WASM_TYPE_I32, expected_type, - " in current_memory"); + push_type(ctx, WASM_CHECK_TYPE_I32); break; case WASM_EXPR_TYPE_NOP: - check_type(ctx, &expr->loc, WASM_TYPE_VOID, expected_type, " in nop"); break; case WASM_EXPR_TYPE_RETURN: { - WasmType result_type = + WasmCheckType result_type = wasm_get_result_type(ctx->current_module, ctx->current_func); - check_expr_opt(ctx, &expr->loc, expr->return_.expr, result_type, - " of return"); + if (result_type != WASM_TYPE_VOID) { + transform_stack(ctx, &expr->loc, "return", 1, 1, result_type, + WASM_CHECK_TYPE_ANY); + } else { + transform_stack(ctx, &expr->loc, "return", 0, 1, WASM_CHECK_TYPE_ANY); + } break; } - case WASM_EXPR_TYPE_SELECT: - check_expr(ctx, expr->select.cond, WASM_TYPE_I32, " of condition"); - check_expr(ctx, expr->select.true_, expected_type, - " of argument 0 of select op"); - check_expr(ctx, expr->select.false_, expected_type, - " of argment 1 of select op"); + case WASM_EXPR_TYPE_SELECT: { + WasmCheckType type = peek_type(ctx, 1, 1); + transform_stack(ctx, &expr->loc, "select", 3, 1, type, type, + WASM_CHECK_TYPE_I32, type); break; + } case WASM_EXPR_TYPE_SET_LOCAL: { - WasmType type; - if (WASM_SUCCEEDED(check_local_var(ctx, &expr->set_local.var, &type))) { - check_type(ctx, &expr->loc, type, expected_type, desc); - check_expr(ctx, expr->set_local.expr, type, " of set_local"); - } + WasmCheckType type = WASM_TYPE_I32; + check_local_var(ctx, &expr->set_local.var, &type); + transform_stack(ctx, &expr->loc, "set_local", 1, 0, type); break; } case WASM_EXPR_TYPE_STORE: { - WasmType rtype = s_opcode_rtype[expr->store.opcode]; - WasmType type1 = s_opcode_type1[expr->store.opcode]; - WasmType type2 = s_opcode_type2[expr->store.opcode]; + WasmCheckType type1 = s_opcode_type1[expr->store.opcode]; + WasmCheckType type2 = s_opcode_type2[expr->store.opcode]; check_align(ctx, &expr->loc, expr->store.align); check_offset(ctx, &expr->loc, expr->store.offset); - check_type(ctx, &expr->loc, rtype, expected_type, desc); - check_expr(ctx, expr->store.addr, type1, " of store index"); - check_expr(ctx, expr->store.value, type2, " of store value"); + transform_stack(ctx, &expr->loc, "store", 2, 0, type1, type2); break; } - case WASM_EXPR_TYPE_BR_TABLE: { - check_expr(ctx, expr->br_table.key, WASM_TYPE_I32, " of key"); - size_t i; - for (i = 0; i < expr->br_table.targets.size; ++i) { - check_br(ctx, &expr->loc, &expr->br_table.targets.data[i], - expr->br_table.expr, " of br_table target"); - } - check_br(ctx, &expr->loc, &expr->br_table.default_target, - expr->br_table.expr, " of br_table default target"); + case WASM_EXPR_TYPE_TEE_LOCAL: { + WasmCheckType type = WASM_TYPE_I32; + check_local_var(ctx, &expr->tee_local.var, &type); + transform_stack(ctx, &expr->loc, "tee_local", 1, 1, type, type); break; } case WASM_EXPR_TYPE_UNARY: { - WasmType rtype = s_opcode_rtype[expr->unary.opcode]; - WasmType type1 = s_opcode_type1[expr->unary.opcode]; - check_type(ctx, &expr->loc, rtype, expected_type, desc); - check_expr(ctx, expr->unary.expr, type1, " of unary op"); + WasmCheckType rtype = s_opcode_rtype[expr->unary.opcode]; + WasmCheckType type1 = s_opcode_type1[expr->unary.opcode]; + transform_stack(ctx, &expr->loc, "unary", 1, 1, type1, rtype); break; } case WASM_EXPR_TYPE_UNREACHABLE: + push_type(ctx, WASM_CHECK_TYPE_ANY); break; } } @@ -705,18 +792,25 @@ static void check_func_signature_matches_func_type( const WasmFuncSignature* sig, const WasmFuncType* func_type) { size_t num_params = sig->param_types.size; - check_type_exact(ctx, loc, sig->result_type, - wasm_get_func_type_result_type(func_type), - " between function result and function type result"); + /* special case this check to give a better error message */ + WasmCheckType func_type_result_type = + wasm_get_func_type_result_type(func_type); + if (sig->result_type != func_type_result_type) { + print_error( + ctx, CHECK_STYLE_FULL, loc, + "type mismatch between function result type (%s) and function type " + "result type (%s)\n", + s_type_names[sig->result_type], s_type_names[func_type_result_type]); + } if (num_params == wasm_get_func_type_num_params(func_type)) { size_t i; for (i = 0; i < num_params; ++i) { - check_type_arg_exact(ctx, loc, sig->param_types.data[i], - wasm_get_func_type_param_type(func_type, i), - "function", i); + check_type_arg(ctx, loc, sig->param_types.data[i], + wasm_get_func_type_param_type(func_type, i), "function", + i); } } else { - print_error(ctx, CHECK_TYPE_FULL, loc, + print_error(ctx, CHECK_STYLE_FULL, loc, "expected %" PRIzd " parameters, got %" PRIzd, wasm_get_func_type_num_params(func_type), num_params); } @@ -743,8 +837,10 @@ static void check_func(Context* ctx, WasmLabel label = wasm_empty_string_slice(); push_label(ctx, &func->loc, &node, &label, func->decl.sig.result_type, "func"); - check_expr_list(ctx, &func->loc, func->first_expr, func->decl.sig.result_type, - " of function result"); + WasmCheckType rtype = + check_block(ctx, &func->loc, func->first_expr, "function result"); + join_type(ctx, &func->loc, rtype, func->decl.sig.result_type, + "function result"); pop_label(ctx); ctx->current_func = NULL; } @@ -768,19 +864,19 @@ static void check_table(Context* ctx, const WasmVarVector* table) { static void check_memory(Context* ctx, const WasmMemory* memory) { if (memory->initial_pages > WASM_MAX_PAGES) { - print_error(ctx, CHECK_TYPE_FULL, &memory->loc, + print_error(ctx, CHECK_STYLE_FULL, &memory->loc, "initial pages (%" PRIu64 ") must be <= (%u)", memory->initial_pages, WASM_MAX_PAGES); } if (memory->max_pages > WASM_MAX_PAGES) { - print_error(ctx, CHECK_TYPE_FULL, &memory->loc, + print_error(ctx, CHECK_STYLE_FULL, &memory->loc, "max pages (%" PRIu64 ") must be <= (%u)", memory->max_pages, WASM_MAX_PAGES); } if (memory->max_pages < memory->initial_pages) { - print_error(ctx, CHECK_TYPE_FULL, &memory->loc, + print_error(ctx, CHECK_STYLE_FULL, &memory->loc, "max pages (%" PRIu64 ") must be >= initial pages (%" PRIu64 ")", memory->max_pages, memory->initial_pages); @@ -791,17 +887,17 @@ static void check_memory(Context* ctx, const WasmMemory* memory) { for (i = 0; i < memory->segments.size; ++i) { const WasmSegment* segment = &memory->segments.data[i]; if (segment->addr < last_end) { - print_error(ctx, CHECK_TYPE_FULL, &segment->loc, + print_error(ctx, CHECK_STYLE_FULL, &segment->loc, "address (%u) less than end of previous segment (%u)", segment->addr, last_end); } if (segment->addr > memory->initial_pages * WASM_PAGE_SIZE) { - print_error(ctx, CHECK_TYPE_FULL, &segment->loc, + print_error(ctx, CHECK_STYLE_FULL, &segment->loc, "address (%u) greater than initial memory size (%" PRIu64 ")", segment->addr, memory->initial_pages * WASM_PAGE_SIZE); } else if (segment->addr + segment->size > memory->initial_pages * WASM_PAGE_SIZE) { - print_error(ctx, CHECK_TYPE_FULL, &segment->loc, + print_error(ctx, CHECK_STYLE_FULL, &segment->loc, "segment ends past the end of initial memory size (%" PRIu64 ")", memory->initial_pages * WASM_PAGE_SIZE); @@ -841,7 +937,7 @@ static void check_module(Context* ctx, const WasmModule* module) { case WASM_MODULE_FIELD_TYPE_TABLE: if (seen_table) { - print_error(ctx, CHECK_TYPE_FULL, &field->loc, + print_error(ctx, CHECK_STYLE_FULL, &field->loc, "only one table allowed"); } check_table(ctx, &field->table); @@ -850,7 +946,7 @@ static void check_module(Context* ctx, const WasmModule* module) { case WASM_MODULE_FIELD_TYPE_MEMORY: if (seen_memory) { - print_error(ctx, CHECK_TYPE_FULL, &field->loc, + print_error(ctx, CHECK_STYLE_FULL, &field->loc, "only one memory block allowed"); } check_memory(ctx, &field->memory); @@ -862,7 +958,7 @@ static void check_module(Context* ctx, const WasmModule* module) { case WASM_MODULE_FIELD_TYPE_START: { if (seen_start) { - print_error(ctx, CHECK_TYPE_FULL, &field->loc, + print_error(ctx, CHECK_STYLE_FULL, &field->loc, "only one start function allowed"); } @@ -870,13 +966,13 @@ static void check_module(Context* ctx, const WasmModule* module) { check_func_var(ctx, &field->start, &start_func); if (start_func) { if (wasm_get_num_params(ctx->current_module, start_func) != 0) { - print_error(ctx, CHECK_TYPE_FULL, &field->loc, + print_error(ctx, CHECK_STYLE_FULL, &field->loc, "start function must be nullary"); } if (wasm_get_result_type(ctx->current_module, start_func) != WASM_TYPE_VOID) { - print_error(ctx, CHECK_TYPE_FULL, &field->loc, + print_error(ctx, CHECK_STYLE_FULL, &field->loc, "start function must not return anything"); } } @@ -887,7 +983,8 @@ static void check_module(Context* ctx, const WasmModule* module) { } if (seen_export_memory && !seen_memory) { - print_error(ctx, CHECK_TYPE_FULL, export_memory_loc, "no memory to export"); + print_error(ctx, CHECK_STYLE_FULL, export_memory_loc, + "no memory to export"); } check_duplicate_bindings(ctx, &module->func_bindings, "function"); @@ -906,10 +1003,10 @@ static void on_read_binary_error(uint32_t offset, void* user_data) { BinaryErrorCallbackData* data = user_data; if (offset == WASM_UNKNOWN_OFFSET) { - print_error(data->ctx, CHECK_TYPE_FULL, data->loc, + print_error(data->ctx, CHECK_STYLE_FULL, data->loc, "error in binary module: %s", error); } else { - print_error(data->ctx, CHECK_TYPE_FULL, data->loc, + print_error(data->ctx, CHECK_STYLE_FULL, data->loc, "error in binary module: @0x%08x: %s", offset, error); } } @@ -938,49 +1035,50 @@ static void check_raw_module(Context* ctx, WasmRawModule* raw) { } } -static void check_invoke(Context* ctx, - const WasmCommandInvoke* invoke, - TypeSet return_type) { +/* returns the result type of the invoked function, checked by the caller; + * returning WASM_CHECK_TYPE_ANY means that another error occured first, so the + * result type should be ignored. */ +static WasmCheckType check_invoke(Context* ctx, + const WasmCommandInvoke* invoke) { const WasmModule* module = ctx->current_module; if (!module) { - print_error(ctx, CHECK_TYPE_FULL, &invoke->loc, + print_error(ctx, CHECK_STYLE_FULL, &invoke->loc, "invoke must occur after a module definition"); - return; + return WASM_CHECK_TYPE_ANY; } - WasmExport* export = - wasm_get_export_by_name(module, &invoke->name); + WasmExport* export = wasm_get_export_by_name(module, &invoke->name); if (!export) { - print_error(ctx, CHECK_TYPE_NAME, &invoke->loc, + print_error(ctx, CHECK_STYLE_NAME, &invoke->loc, "unknown function export \"" PRIstringslice "\"", WASM_PRINTF_STRING_SLICE_ARG(invoke->name)); - return; + return WASM_CHECK_TYPE_ANY; } WasmFunc* func = wasm_get_func_by_var(module, &export->var); if (!func) { /* this error will have already been reported, just skip it */ - return; + return WASM_CHECK_TYPE_ANY; } size_t actual_args = invoke->args.size; size_t expected_args = wasm_get_num_params(module, func); if (expected_args != actual_args) { - print_error(ctx, CHECK_TYPE_FULL, &invoke->loc, + print_error(ctx, CHECK_STYLE_FULL, &invoke->loc, "too %s parameters to function. got %" PRIzd ", expected %" PRIzd, actual_args > expected_args ? "many" : "few", actual_args, expected_args); - return; + return WASM_CHECK_TYPE_ANY; } - check_type_set(ctx, &invoke->loc, wasm_get_result_type(module, func), - return_type, ""); size_t i; for (i = 0; i < actual_args; ++i) { WasmConst* const_ = &invoke->args.data[i]; - check_type_arg_exact(ctx, &const_->loc, const_->type, - wasm_get_param_type(module, func, i), "invoke", i); + check_type_arg(ctx, &const_->loc, const_->type, + wasm_get_param_type(module, func, i), "invoke", i); } + + return wasm_get_result_type(module, func); } static void check_command(Context* ctx, const WasmCommand* command) { @@ -990,25 +1088,36 @@ static void check_command(Context* ctx, const WasmCommand* command) { break; case WASM_COMMAND_TYPE_INVOKE: - check_invoke(ctx, &command->invoke, WASM_TYPE_SET_VOID); + /* ignore result type */ + check_invoke(ctx, &command->invoke); break; case WASM_COMMAND_TYPE_ASSERT_INVALID: /* ignore */ break; - case WASM_COMMAND_TYPE_ASSERT_RETURN: - check_invoke(ctx, &command->assert_return.invoke, - TYPE_TO_TYPE_SET(command->assert_return.expected.type)); + case WASM_COMMAND_TYPE_ASSERT_RETURN: { + const WasmCommandInvoke* invoke = &command->assert_return.invoke; + WasmCheckType result_type = check_invoke(ctx, invoke); + if (result_type != WASM_CHECK_TYPE_ANY) { + check_type(ctx, &invoke->loc, result_type, + command->assert_return.expected.type, "invoke"); + } break; + } - case WASM_COMMAND_TYPE_ASSERT_RETURN_NAN: - check_invoke(ctx, &command->assert_return_nan.invoke, - WASM_TYPE_SET_F32 | WASM_TYPE_SET_F64); + case WASM_COMMAND_TYPE_ASSERT_RETURN_NAN: { + const WasmCommandInvoke* invoke = &command->assert_return_nan.invoke; + WasmCheckType result_type = check_invoke(ctx, invoke); + if (result_type != WASM_CHECK_TYPE_ANY) { + check_assert_return_nan_type(ctx, &invoke->loc, result_type, "invoke"); + } break; + } case WASM_COMMAND_TYPE_ASSERT_TRAP: - check_invoke(ctx, &command->assert_trap.invoke, WASM_TYPE_SET_VOID); + /* ignore result type */ + check_invoke(ctx, &command->assert_trap.invoke); break; default: @@ -1016,12 +1125,14 @@ static void check_command(Context* ctx, const WasmCommand* command) { } } -WasmResult wasm_check_names(WasmAstLexer* lexer, +WasmResult wasm_check_names(WasmAllocator* allocator, + WasmAstLexer* lexer, const struct WasmScript* script, WasmSourceErrorHandler* error_handler) { Context ctx; WASM_ZERO_MEMORY(ctx); - ctx.check_type = CHECK_TYPE_NAME; + ctx.check_style = CHECK_STYLE_NAME; + ctx.allocator = allocator; ctx.lexer = lexer; ctx.error_handler = error_handler; ctx.result = WASM_OK; @@ -1031,12 +1142,14 @@ WasmResult wasm_check_names(WasmAstLexer* lexer, return ctx.result; } -WasmResult wasm_check_ast(WasmAstLexer* lexer, +WasmResult wasm_check_ast(WasmAllocator* allocator, + WasmAstLexer* lexer, const struct WasmScript* script, WasmSourceErrorHandler* error_handler) { Context ctx; WASM_ZERO_MEMORY(ctx); - ctx.check_type = CHECK_TYPE_FULL; + ctx.check_style = CHECK_STYLE_FULL; + ctx.allocator = allocator; ctx.lexer = lexer; ctx.error_handler = error_handler; ctx.result = WASM_OK; @@ -1054,7 +1167,7 @@ WasmResult wasm_check_assert_invalid( WasmSourceErrorHandler* error_handler) { Context ctx; WASM_ZERO_MEMORY(ctx); - ctx.check_type = CHECK_TYPE_FULL; + ctx.check_style = CHECK_STYLE_FULL; ctx.allocator = allocator; ctx.lexer = lexer; ctx.error_handler = error_handler; @@ -1068,7 +1181,7 @@ WasmResult wasm_check_assert_invalid( Context ai_ctx; WASM_ZERO_MEMORY(ai_ctx); - ai_ctx.check_type = CHECK_TYPE_FULL; + ai_ctx.check_style = CHECK_STYLE_FULL; ai_ctx.allocator = allocator; ai_ctx.lexer = lexer; ai_ctx.error_handler = assert_invalid_error_handler; @@ -1076,7 +1189,7 @@ WasmResult wasm_check_assert_invalid( check_raw_module(&ai_ctx, &command->assert_invalid.module); if (WASM_SUCCEEDED(ai_ctx.result)) { - print_error(&ctx, CHECK_TYPE_FULL, &command->assert_invalid.module.loc, + print_error(&ctx, CHECK_STYLE_FULL, &command->assert_invalid.module.loc, "expected module to be invalid"); } } |