diff --git a/include/wabt/ir.h b/include/wabt/ir.h index dc04577c3f..35f6593571 100644 --- a/include/wabt/ir.h +++ b/include/wabt/ir.h @@ -701,7 +701,13 @@ class SelectExpr : public ExprMixin { public: SelectExpr(const Location& loc = Location()) : ExprMixin(loc) {} - TypeVector result_type; + // Untyped select is represented by {Type::Void}. An empty result_type + // represents an explicit typed select with zero result types and is invalid. + TypeVector result_type{Type::Void}; + + bool IsUntyped() const { + return result_type.size() == 1 && result_type[0] == Type::Void; + } }; class TableInitExpr : public ExprMixin { diff --git a/include/wabt/shared-validator.h b/include/wabt/shared-validator.h index c8e78ac96d..91d33d34c8 100644 --- a/include/wabt/shared-validator.h +++ b/include/wabt/shared-validator.h @@ -197,6 +197,7 @@ class SharedValidator { Result OnReturnCallRef(const Location&, Var function_type_var); Result OnReturn(const Location&); Result OnSelect(const Location&, Index result_count, Type* result_types); + Result OnSelectCondition(const Location&); Result OnSimdLaneOp(const Location&, Opcode, uint64_t lane_idx); Result OnSimdLoadLane(const Location&, Opcode, diff --git a/include/wabt/type-checker.h b/include/wabt/type-checker.h index 723e5d90d1..59a6353c6d 100644 --- a/include/wabt/type-checker.h +++ b/include/wabt/type-checker.h @@ -148,6 +148,7 @@ class TypeChecker { Result OnRethrow(Index depth); Result OnReturn(); Result OnSelect(const TypeVector& result_types); + Result OnSelectCondition(); Result OnSimdLaneOp(Opcode, uint64_t); Result OnSimdLoadLane(Opcode, const Limits& limits, uint64_t); Result OnSimdStoreLane(Opcode, const Limits& limits, uint64_t); diff --git a/src/binary-reader-ir.cc b/src/binary-reader-ir.cc index 232671e32b..633e182278 100644 --- a/src/binary-reader-ir.cc +++ b/src/binary-reader-ir.cc @@ -1212,7 +1212,9 @@ Result BinaryReaderIR::OnReturnExpr() { Result BinaryReaderIR::OnSelectExpr(Index result_count, Type* result_types) { auto expr_ptr = std::make_unique(); - expr_ptr->result_type.assign(result_types, result_types + result_count); + if (result_count != 0) { + expr_ptr->result_type.assign(result_types, result_types + result_count); + } return AppendExpr(std::move(expr_ptr)); } diff --git a/src/binary-reader.cc b/src/binary-reader.cc index 8c28d20b6b..084c8bb377 100644 --- a/src/binary-reader.cc +++ b/src/binary-reader.cc @@ -813,6 +813,7 @@ Result BinaryReader::ReadInstructions(Offset end_offset, const char* context) { case Opcode::SelectT: { Index num_results; CHECK_RESULT(ReadCount(&num_results, "num result types")); + ERROR_IF(num_results == 0, "invalid arity in select instruction: 0."); result_types_.resize(num_results); for (Index i = 0; i < num_results; ++i) { @@ -825,13 +826,8 @@ Result BinaryReader::ReadInstructions(Offset end_offset, const char* context) { result_types_[i] = result_type; } - if (num_results) { - CALLBACK(OnSelectExpr, num_results, result_types_.data()); - CALLBACK(OnOpcodeType, result_types_[0]); - } else { - CALLBACK(OnSelectExpr, 0, NULL); - CALLBACK0(OnOpcodeBare); - } + CALLBACK(OnSelectExpr, num_results, result_types_.data()); + CALLBACK(OnOpcodeType, result_types_[0]); break; } diff --git a/src/binary-writer.cc b/src/binary-writer.cc index b2cfa5ad90..93b24a1ccc 100644 --- a/src/binary-writer.cc +++ b/src/binary-writer.cc @@ -1081,7 +1081,7 @@ void BinaryWriter::WriteExpr(const Func* func, const Expr* expr) { break; case ExprType::Select: { auto* select_expr = cast(expr); - if (select_expr->result_type.empty()) { + if (select_expr->IsUntyped()) { WriteOpcode(stream_, Opcode::Select); } else { WriteOpcode(stream_, Opcode::SelectT); diff --git a/src/shared-validator.cc b/src/shared-validator.cc index 10f977c3aa..fc7f3e3fac 100644 --- a/src/shared-validator.cc +++ b/src/shared-validator.cc @@ -1309,6 +1309,12 @@ Result SharedValidator::OnSelect(const Location& loc, return result; } +Result SharedValidator::OnSelectCondition(const Location& loc) { + Result result = CheckInstr(Opcode::Select, loc); + result |= typechecker_.OnSelectCondition(); + return result; +} + Result SharedValidator::OnSimdLaneOp(const Location& loc, Opcode opcode, uint64_t value) { diff --git a/src/type-checker.cc b/src/type-checker.cc index de097dd238..ada0ab0388 100644 --- a/src/type-checker.cc +++ b/src/type-checker.cc @@ -1061,6 +1061,10 @@ Result TypeChecker::OnSelect(const TypeVector& expected) { return result; } +Result TypeChecker::OnSelectCondition() { + return PopAndCheck1Type(Type::I32, "select"); +} + Result TypeChecker::OnStore(Opcode opcode, const Limits& limits) { return CheckOpcode2(opcode, &limits); } diff --git a/src/validator.cc b/src/validator.cc index 504690ae77..295f1c28d6 100644 --- a/src/validator.cc +++ b/src/validator.cc @@ -589,8 +589,21 @@ Result Validator::OnReturnCallRefExpr(ReturnCallRefExpr* expr) { } Result Validator::OnSelectExpr(SelectExpr* expr) { - result_ |= validator_.OnSelect(expr->loc, expr->result_type.size(), - expr->result_type.data()); + if (expr->result_type.empty()) { + validator_.PrintError(expr->loc, "invalid arity in select instruction: 0."); + result_ |= Result::Error; + result_ |= validator_.OnSelectCondition(expr->loc); + return Result::Ok; + } + + Index result_count = 0; + Type* result_types = nullptr; + if (!expr->IsUntyped()) { + result_count = expr->result_type.size(); + result_types = expr->result_type.data(); + } + + result_ |= validator_.OnSelect(expr->loc, result_count, result_types); return Result::Ok; } diff --git a/src/wast-parser.cc b/src/wast-parser.cc index 9272de391b..4fcfd4dbdc 100644 --- a/src/wast-parser.cc +++ b/src/wast-parser.cc @@ -2538,6 +2538,7 @@ Result WastParser::ParsePlainInstr(std::unique_ptr* out_expr) { ResolveTypeVector result_type(&expr->result_type); if (options_->features.reference_types_enabled() && PeekMatchLpar(TokenType::Result)) { + expr->result_type.clear(); CHECK_RESULT(ParseResultList(&expr->result_type, &result_type.vars)); } *out_expr = std::move(expr); diff --git a/src/wat-writer.cc b/src/wat-writer.cc index 14b9e024ca..77ac71aaef 100644 --- a/src/wat-writer.cc +++ b/src/wat-writer.cc @@ -977,7 +977,10 @@ Result WatWriter::ExprVisitorDelegate::OnReturnCallRefExpr( Result WatWriter::ExprVisitorDelegate::OnSelectExpr(SelectExpr* expr) { writer_->WritePutsSpace(Opcode::Select_Opcode.GetName()); - if (!expr->result_type.empty()) { + if (expr->result_type.empty()) { + writer_->WriteOpenSpace("result"); + writer_->WriteCloseSpace(); + } else if (!expr->IsUntyped()) { writer_->WriteTypes(expr->result_type, "result"); } writer_->WriteNewline(NO_FORCE_NEWLINE); diff --git a/test/binary/bad-select-empty-result.txt b/test/binary/bad-select-empty-result.txt new file mode 100644 index 0000000000..06353dc447 --- /dev/null +++ b/test/binary/bad-select-empty-result.txt @@ -0,0 +1,26 @@ +;;; TOOL: run-gen-wasm-bad +;;; ERROR: 1 +;;; RUN: %(wasm-interp)s --enable-all %(temp_file)s.wasm --run-export=run +;;; ERROR3: 1 +magic +version +section(TYPE) { count[1] function params[0] results[1] i32 } +section(FUNCTION) { count[1] type[0] } +section(EXPORT) { count[1] str("run") func_kind 0 } +section(CODE) { + count[1] + func { + locals[0] + i32.const 42 + i32.const 100 + i32.const 1 + select_t[0x1c] + result_count[0x00] + end + } +} +(;; STDERR ;;; +0000029: error: invalid arity in select instruction: 0. +0000029: error: invalid arity in select instruction: 0. +0000029: error: invalid arity in select instruction: 0. +;;; STDERR ;;) diff --git a/test/parse/expr/bad-select-empty-result.txt b/test/parse/expr/bad-select-empty-result.txt new file mode 100644 index 0000000000..da35bb53d8 --- /dev/null +++ b/test/parse/expr/bad-select-empty-result.txt @@ -0,0 +1,15 @@ +;;; TOOL: wat2wasm +;;; ERROR: 1 +(module + (func + i32.const 42 + i32.const 100 + i32.const 1 + select (result) + drop + drop)) +(;; STDERR ;;; +out/test/parse/expr/bad-select-empty-result.txt:8:5: error: invalid arity in select instruction: 0. + select (result) + ^^^^^^ +;;; STDERR ;;) diff --git a/test/spec/function-references/select.txt b/test/spec/function-references/select.txt index 77e04f2482..554f55e796 100644 --- a/test/spec/function-references/select.txt +++ b/test/spec/function-references/select.txt @@ -12,8 +12,7 @@ out/test/spec/function-references/select.wast:364: assert_invalid passed: out/test/spec/function-references/select/select.1.wasm:000001c: error: type mismatch in select, expected [any, any, i32] but got [i32] 000001c: error: OnSelectExpr callback failed out/test/spec/function-references/select.wast:368: assert_invalid passed: - out/test/spec/function-references/select/select.2.wasm:000001c: error: type mismatch in select, expected [any, any, i32] but got [i32] - 000001c: error: OnSelectExpr callback failed + 000001d: error: invalid arity in select instruction: 0. out/test/spec/function-references/select.wast:372: assert_invalid passed: out/test/spec/function-references/select/select.3.wasm:0000027: error: invalid arity in select instruction: 2. 0000027: error: OnSelectExpr callback failed diff --git a/test/spec/select.txt b/test/spec/select.txt index 1418938b88..f242c98c92 100644 --- a/test/spec/select.txt +++ b/test/spec/select.txt @@ -7,8 +7,7 @@ out/test/spec/select.wast:320: assert_invalid passed: out/test/spec/select/select.1.wasm:000001c: error: type mismatch in select, expected [any, any, i32] but got [i32] 000001c: error: OnSelectExpr callback failed out/test/spec/select.wast:324: assert_invalid passed: - out/test/spec/select/select.2.wasm:000001c: error: type mismatch in select, expected [any, any, i32] but got [i32] - 000001c: error: OnSelectExpr callback failed + 000001d: error: invalid arity in select instruction: 0. out/test/spec/select.wast:328: assert_invalid passed: out/test/spec/select/select.3.wasm:0000027: error: invalid arity in select instruction: 2. 0000027: error: OnSelectExpr callback failed