2 * Copyright 2017 WebAssembly Community Group participants
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "wabt/type-checker.h"
25 std::string
TypesToString(const TypeVector
& types
,
26 const char* prefix
= nullptr) {
27 std::string result
= "[";
32 for (size_t i
= 0; i
< types
.size(); ++i
) {
33 result
+= types
[i
].GetName();
34 if (i
< types
.size() - 1) {
42 } // end anonymous namespace
44 TypeChecker::Label::Label(LabelType label_type
,
45 const TypeVector
& param_types
,
46 const TypeVector
& result_types
,
48 : label_type(label_type
),
49 param_types(param_types
),
50 result_types(result_types
),
51 type_stack_limit(limit
),
54 void TypeChecker::PrintError(const char* fmt
, ...) {
55 if (error_callback_
) {
56 WABT_SNPRINTF_ALLOCA(buffer
, length
, fmt
);
57 error_callback_(buffer
);
61 Result
TypeChecker::GetLabel(Index depth
, Label
** out_label
) {
62 if (depth
>= label_stack_
.size()) {
63 assert(label_stack_
.size() > 0);
64 PrintError("invalid depth: %" PRIindex
" (max %" PRIzd
")", depth
,
65 label_stack_
.size() - 1);
69 *out_label
= &label_stack_
[label_stack_
.size() - depth
- 1];
73 Result
TypeChecker::GetRethrowLabel(Index depth
, Label
** out_label
) {
74 if (Failed(GetLabel(depth
, out_label
))) {
78 if ((*out_label
)->label_type
== LabelType::Catch
) {
82 std::string candidates
;
83 for (Index idx
= 0; idx
< label_stack_
.size(); idx
++) {
84 LabelType type
= label_stack_
[label_stack_
.size() - idx
- 1].label_type
;
85 if (type
== LabelType::Catch
) {
86 if (!candidates
.empty()) {
87 candidates
.append(", ");
89 candidates
.append(std::to_string(idx
));
93 if (candidates
.empty()) {
94 PrintError("rethrow not in try catch block");
96 PrintError("invalid rethrow depth: %" PRIindex
" (catches: %s)", depth
,
100 return Result::Error
;
103 Result
TypeChecker::GetCatchCount(Index depth
, Index
* out_count
) {
105 if (Failed(GetLabel(depth
, &unused
))) {
106 return Result::Error
;
109 Index catch_count
= 0;
110 for (Index idx
= 0; idx
<= depth
; idx
++) {
111 LabelType type
= label_stack_
[label_stack_
.size() - idx
- 1].label_type
;
112 if (type
== LabelType::Catch
) {
116 *out_count
= catch_count
;
121 Result
TypeChecker::TopLabel(Label
** out_label
) {
122 return GetLabel(0, out_label
);
125 bool TypeChecker::IsUnreachable() {
127 if (Failed(TopLabel(&label
))) {
130 return label
->unreachable
;
133 void TypeChecker::ResetTypeStackToLabel(Label
* label
) {
134 type_stack_
.resize(label
->type_stack_limit
);
137 Result
TypeChecker::SetUnreachable() {
139 CHECK_RESULT(TopLabel(&label
));
140 label
->unreachable
= true;
141 ResetTypeStackToLabel(label
);
145 void TypeChecker::PushLabel(LabelType label_type
,
146 const TypeVector
& param_types
,
147 const TypeVector
& result_types
) {
148 label_stack_
.emplace_back(label_type
, param_types
, result_types
,
152 Result
TypeChecker::PopLabel() {
153 label_stack_
.pop_back();
157 Result
TypeChecker::CheckLabelType(Label
* label
, LabelType label_type
) {
158 return label
->label_type
== label_type
? Result::Ok
: Result::Error
;
161 Result
TypeChecker::Check2LabelTypes(Label
* label
,
162 LabelType label_type1
,
163 LabelType label_type2
) {
164 return label
->label_type
== label_type1
|| label
->label_type
== label_type2
169 Result
TypeChecker::GetThisFunctionLabel(Label
** label
) {
170 return GetLabel(label_stack_
.size() - 1, label
);
173 Result
TypeChecker::PeekType(Index depth
, Type
* out_type
) {
175 CHECK_RESULT(TopLabel(&label
));
177 if (label
->type_stack_limit
+ depth
>= type_stack_
.size()) {
178 *out_type
= Type::Any
;
179 return label
->unreachable
? Result::Ok
: Result::Error
;
181 *out_type
= type_stack_
[type_stack_
.size() - depth
- 1];
185 Result
TypeChecker::PeekAndCheckType(Index depth
, Type expected
) {
186 Type actual
= Type::Any
;
187 Result result
= PeekType(depth
, &actual
);
188 return result
| CheckType(actual
, expected
);
191 Result
TypeChecker::DropTypes(size_t drop_count
) {
193 CHECK_RESULT(TopLabel(&label
));
194 if (label
->type_stack_limit
+ drop_count
> type_stack_
.size()) {
195 ResetTypeStackToLabel(label
);
196 return label
->unreachable
? Result::Ok
: Result::Error
;
198 type_stack_
.erase(type_stack_
.end() - drop_count
, type_stack_
.end());
202 void TypeChecker::PushType(Type type
) {
203 if (type
!= Type::Void
) {
204 type_stack_
.push_back(type
);
208 void TypeChecker::PushTypes(const TypeVector
& types
) {
209 for (Type type
: types
) {
214 Result
TypeChecker::CheckTypeStackEnd(const char* desc
) {
216 CHECK_RESULT(TopLabel(&label
));
217 Result result
= (type_stack_
.size() == label
->type_stack_limit
)
220 PrintStackIfFailedV(result
, desc
, {}, /*is_end=*/true);
224 Result
TypeChecker::CheckType(Type actual
, Type expected
) {
225 if (expected
== Type::Any
|| actual
== Type::Any
) {
229 if (expected
== Type::Reference
&& actual
== Type::Reference
) {
230 return expected
.GetReferenceIndex() == actual
.GetReferenceIndex()
234 if (actual
!= expected
) {
235 return Result::Error
;
240 Result
TypeChecker::CheckTypes(const TypeVector
& actual
,
241 const TypeVector
& expected
) {
242 if (actual
.size() != expected
.size()) {
243 return Result::Error
;
245 Result result
= Result::Ok
;
246 for (size_t i
= 0; i
< actual
.size(); i
++)
247 result
|= CheckType(actual
[i
], expected
[i
]);
252 Result
TypeChecker::CheckSignature(const TypeVector
& sig
, const char* desc
) {
253 Result result
= Result::Ok
;
254 for (size_t i
= 0; i
< sig
.size(); ++i
) {
255 result
|= PeekAndCheckType(sig
.size() - i
- 1, sig
[i
]);
257 PrintStackIfFailed(result
, desc
, sig
);
261 Result
TypeChecker::CheckReturnSignature(const TypeVector
& actual
,
262 const TypeVector
& expected
,
264 Result result
= CheckTypes(actual
, expected
);
265 if (Failed(result
)) {
266 PrintError("return signatures have inconsistent types: expected %s, got %s",
267 TypesToString(expected
).c_str(), TypesToString(actual
).c_str());
272 Result
TypeChecker::PopAndCheckSignature(const TypeVector
& sig
,
274 Result result
= CheckSignature(sig
, desc
);
275 result
|= DropTypes(sig
.size());
279 Result
TypeChecker::PopAndCheckCall(const TypeVector
& param_types
,
280 const TypeVector
& result_types
,
282 Result result
= CheckSignature(param_types
, desc
);
283 result
|= DropTypes(param_types
.size());
284 PushTypes(result_types
);
288 Result
TypeChecker::PopAndCheck1Type(Type expected
, const char* desc
) {
289 Result result
= Result::Ok
;
290 result
|= PeekAndCheckType(0, expected
);
291 PrintStackIfFailed(result
, desc
, expected
);
292 result
|= DropTypes(1);
296 Result
TypeChecker::PopAndCheck2Types(Type expected1
,
299 Result result
= Result::Ok
;
300 result
|= PeekAndCheckType(0, expected2
);
301 result
|= PeekAndCheckType(1, expected1
);
302 PrintStackIfFailed(result
, desc
, expected1
, expected2
);
303 result
|= DropTypes(2);
307 Result
TypeChecker::PopAndCheck3Types(Type expected1
,
311 Result result
= Result::Ok
;
312 result
|= PeekAndCheckType(0, expected3
);
313 result
|= PeekAndCheckType(1, expected2
);
314 result
|= PeekAndCheckType(2, expected1
);
315 PrintStackIfFailed(result
, desc
, expected1
, expected2
, expected3
);
316 result
|= DropTypes(3);
320 // Some paramater types depend on the memory being used.
321 // For example load/store operands, or memory.fill operands.
322 static Type
GetMemoryParam(Type param
, const Limits
* limits
) {
323 return limits
? limits
->IndexType() : param
;
326 Result
TypeChecker::CheckOpcode1(Opcode opcode
, const Limits
* limits
) {
327 Result result
= PopAndCheck1Type(
328 GetMemoryParam(opcode
.GetParamType1(), limits
), opcode
.GetName());
329 PushType(opcode
.GetResultType());
333 Result
TypeChecker::CheckOpcode2(Opcode opcode
, const Limits
* limits
) {
335 PopAndCheck2Types(GetMemoryParam(opcode
.GetParamType1(), limits
),
336 opcode
.GetParamType2(), opcode
.GetName());
337 PushType(opcode
.GetResultType());
341 Result
TypeChecker::CheckOpcode3(Opcode opcode
,
342 const Limits
* limits1
,
343 const Limits
* limits2
,
344 const Limits
* limits3
) {
345 Result result
= PopAndCheck3Types(
346 GetMemoryParam(opcode
.GetParamType1(), limits1
),
347 GetMemoryParam(opcode
.GetParamType2(), limits2
),
348 GetMemoryParam(opcode
.GetParamType3(), limits3
), opcode
.GetName());
349 PushType(opcode
.GetResultType());
353 void TypeChecker::PrintStackIfFailedV(Result result
,
355 const TypeVector
& expected
,
357 if (Succeeded(result
)) {
363 if (Succeeded(TopLabel(&label
))) {
364 limit
= label
->type_stack_limit
;
368 size_t max_depth
= type_stack_
.size() - limit
;
370 // In general we want to print as many values of the actual stack as were
371 // expected. However, if the stack was expected to be empty, we should
372 // print some amount of the actual stack.
374 if (expected
.size() == 0) {
375 // Don't print too many elements if the stack is really deep.
376 const size_t kMaxActualStackToPrint
= 4;
377 actual_size
= std::min(kMaxActualStackToPrint
, max_depth
);
379 actual_size
= std::min(expected
.size(), max_depth
);
382 bool incomplete_actual_stack
= actual_size
!= max_depth
;
384 for (size_t i
= 0; i
< actual_size
; ++i
) {
386 Result result
= PeekType(actual_size
- i
- 1, &type
);
388 assert(Succeeded(result
));
389 actual
.push_back(type
);
392 std::string message
= "type mismatch in ";
394 message
= "type mismatch at end of ";
397 message
+= ", expected ";
398 message
+= TypesToString(expected
);
399 message
+= " but got ";
400 message
+= TypesToString(actual
, incomplete_actual_stack
? "... " : nullptr);
402 PrintError("%s", message
.c_str());
405 Result
TypeChecker::BeginFunction(const TypeVector
& sig
) {
407 label_stack_
.clear();
408 PushLabel(LabelType::Func
, TypeVector(), sig
);
412 Result
TypeChecker::OnAtomicLoad(Opcode opcode
, const Limits
& limits
) {
413 return CheckOpcode1(opcode
, &limits
);
416 Result
TypeChecker::OnAtomicStore(Opcode opcode
, const Limits
& limits
) {
417 return CheckOpcode2(opcode
, &limits
);
420 Result
TypeChecker::OnAtomicRmw(Opcode opcode
, const Limits
& limits
) {
421 return CheckOpcode2(opcode
, &limits
);
424 Result
TypeChecker::OnAtomicRmwCmpxchg(Opcode opcode
, const Limits
& limits
) {
425 return CheckOpcode3(opcode
, &limits
);
428 Result
TypeChecker::OnAtomicWait(Opcode opcode
, const Limits
& limits
) {
429 return CheckOpcode3(opcode
, &limits
);
432 Result
TypeChecker::OnAtomicFence(uint32_t consistency_model
) {
436 Result
TypeChecker::OnAtomicNotify(Opcode opcode
, const Limits
& limits
) {
437 return CheckOpcode2(opcode
, &limits
);
440 Result
TypeChecker::OnBinary(Opcode opcode
) {
441 return CheckOpcode2(opcode
);
444 Result
TypeChecker::OnBlock(const TypeVector
& param_types
,
445 const TypeVector
& result_types
) {
446 Result result
= PopAndCheckSignature(param_types
, "block");
447 PushLabel(LabelType::Block
, param_types
, result_types
);
448 PushTypes(param_types
);
452 Result
TypeChecker::OnBr(Index depth
) {
453 Result result
= Result::Ok
;
455 CHECK_RESULT(GetLabel(depth
, &label
));
456 result
|= CheckSignature(label
->br_types(), "br");
457 CHECK_RESULT(SetUnreachable());
461 Result
TypeChecker::OnBrIf(Index depth
) {
462 Result result
= PopAndCheck1Type(Type::I32
, "br_if");
464 CHECK_RESULT(GetLabel(depth
, &label
));
465 result
|= PopAndCheckSignature(label
->br_types(), "br_if");
466 PushTypes(label
->br_types());
470 Result
TypeChecker::BeginBrTable() {
471 br_table_sig_
= nullptr;
472 return PopAndCheck1Type(Type::I32
, "br_table");
475 Result
TypeChecker::OnBrTableTarget(Index depth
) {
476 Result result
= Result::Ok
;
478 CHECK_RESULT(GetLabel(depth
, &label
));
479 TypeVector
& label_sig
= label
->br_types();
480 result
|= CheckSignature(label_sig
, "br_table");
482 // Make sure this label's signature is consistent with the previous labels'
484 if (br_table_sig_
== nullptr) {
485 br_table_sig_
= &label_sig
;
487 if (br_table_sig_
->size() != label_sig
.size()) {
488 result
|= Result::Error
;
489 PrintError("br_table labels have inconsistent types: expected %s, got %s",
490 TypesToString(*br_table_sig_
).c_str(),
491 TypesToString(label_sig
).c_str());
498 Result
TypeChecker::EndBrTable() {
499 return SetUnreachable();
502 Result
TypeChecker::OnCall(const TypeVector
& param_types
,
503 const TypeVector
& result_types
) {
504 return PopAndCheckCall(param_types
, result_types
, "call");
507 Result
TypeChecker::OnCallIndirect(const TypeVector
& param_types
,
508 const TypeVector
& result_types
) {
509 Result result
= PopAndCheck1Type(Type::I32
, "call_indirect");
510 result
|= PopAndCheckCall(param_types
, result_types
, "call_indirect");
514 Result
TypeChecker::OnIndexedFuncRef(Index
* out_index
) {
516 CHECK_RESULT(PeekType(0, &type
));
517 Result result
= Result::Ok
;
518 if (!(type
== Type::Any
|| type
.IsReferenceWithIndex())) {
520 actual
.push_back(type
);
521 std::string message
=
522 "type mismatch in call_ref, expected reference but got " +
523 TypesToString(actual
);
524 PrintError("%s", message
.c_str());
525 result
= Result::Error
;
527 if (Succeeded(result
)) {
528 *out_index
= type
.GetReferenceIndex();
530 result
|= DropTypes(1);
534 Result
TypeChecker::OnReturnCall(const TypeVector
& param_types
,
535 const TypeVector
& result_types
) {
536 Result result
= PopAndCheckSignature(param_types
, "return_call");
538 CHECK_RESULT(GetThisFunctionLabel(&func_label
));
539 result
|= CheckReturnSignature(result_types
, func_label
->result_types
,
542 CHECK_RESULT(SetUnreachable());
546 Result
TypeChecker::OnReturnCallIndirect(const TypeVector
& param_types
,
547 const TypeVector
& result_types
) {
548 Result result
= PopAndCheck1Type(Type::I32
, "return_call_indirect");
550 result
|= PopAndCheckSignature(param_types
, "return_call_indirect");
552 CHECK_RESULT(GetThisFunctionLabel(&func_label
));
553 result
|= CheckReturnSignature(result_types
, func_label
->result_types
,
554 "return_call_indirect");
556 CHECK_RESULT(SetUnreachable());
560 Result
TypeChecker::OnCompare(Opcode opcode
) {
561 return CheckOpcode2(opcode
);
564 Result
TypeChecker::OnCatch(const TypeVector
& sig
) {
565 Result result
= Result::Ok
;
567 CHECK_RESULT(TopLabel(&label
));
568 result
|= Check2LabelTypes(label
, LabelType::Try
, LabelType::Catch
);
569 result
|= PopAndCheckSignature(label
->result_types
, "try block");
570 result
|= CheckTypeStackEnd("try block");
571 ResetTypeStackToLabel(label
);
572 label
->label_type
= LabelType::Catch
;
573 label
->unreachable
= false;
578 Result
TypeChecker::OnConst(Type type
) {
583 Result
TypeChecker::OnConvert(Opcode opcode
) {
584 return CheckOpcode1(opcode
);
587 Result
TypeChecker::OnDelegate(Index depth
) {
588 Result result
= Result::Ok
;
590 // Delegate starts counting after the current try, as the delegate
591 // instruction is not actually in the try block.
592 CHECK_RESULT(GetLabel(depth
+ 1, &label
));
595 CHECK_RESULT(TopLabel(&try_label
));
596 result
|= CheckLabelType(try_label
, LabelType::Try
);
597 result
|= PopAndCheckSignature(try_label
->result_types
, "try block");
598 result
|= CheckTypeStackEnd("try block");
599 ResetTypeStackToLabel(try_label
);
601 // Since an end instruction does not follow a delegate, we push
602 // the block results here and pop the label.
603 PushTypes(try_label
->result_types
);
608 Result
TypeChecker::OnDrop() {
609 Result result
= Result::Ok
;
610 result
|= DropTypes(1);
611 PrintStackIfFailed(result
, "drop", Type::Any
);
615 Result
TypeChecker::OnElse() {
616 Result result
= Result::Ok
;
618 CHECK_RESULT(TopLabel(&label
));
619 result
|= CheckLabelType(label
, LabelType::If
);
620 result
|= PopAndCheckSignature(label
->result_types
, "`if true` branch");
621 result
|= CheckTypeStackEnd("`if true` branch");
622 ResetTypeStackToLabel(label
);
623 PushTypes(label
->param_types
);
624 label
->label_type
= LabelType::Else
;
625 label
->unreachable
= false;
629 Result
TypeChecker::OnEnd(Label
* label
,
630 const char* sig_desc
,
631 const char* end_desc
) {
632 Result result
= Result::Ok
;
633 result
|= PopAndCheckSignature(label
->result_types
, sig_desc
);
634 result
|= CheckTypeStackEnd(end_desc
);
635 ResetTypeStackToLabel(label
);
636 PushTypes(label
->result_types
);
641 Result
TypeChecker::OnEnd() {
642 Result result
= Result::Ok
;
643 static const char* s_label_type_name
[] = {
644 "function", "initializer expression", "block", "loop",
645 "if", "`if false` branch", "try", "try catch"};
646 WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_label_type_name
) == kLabelTypeCount
);
648 CHECK_RESULT(TopLabel(&label
));
649 assert(static_cast<int>(label
->label_type
) < kLabelTypeCount
);
650 if (label
->label_type
== LabelType::If
) {
651 // An if without an else will just pass the params through, so the result
652 // types must be the same as the param types. It has the same behavior as
653 // an empty else block.
654 CHECK_RESULT(OnElse());
657 const char* desc
= s_label_type_name
[static_cast<int>(label
->label_type
)];
658 result
|= OnEnd(label
, desc
, desc
);
662 Result
TypeChecker::OnIf(const TypeVector
& param_types
,
663 const TypeVector
& result_types
) {
664 Result result
= PopAndCheck1Type(Type::I32
, "if");
665 result
|= PopAndCheckSignature(param_types
, "if");
666 PushLabel(LabelType::If
, param_types
, result_types
);
667 PushTypes(param_types
);
671 Result
TypeChecker::OnGlobalGet(Type type
) {
676 Result
TypeChecker::OnGlobalSet(Type type
) {
677 return PopAndCheck1Type(type
, "global.set");
680 Result
TypeChecker::OnLoad(Opcode opcode
, const Limits
& limits
) {
681 return CheckOpcode1(opcode
, &limits
);
684 Result
TypeChecker::OnLocalGet(Type type
) {
689 Result
TypeChecker::OnLocalSet(Type type
) {
690 return PopAndCheck1Type(type
, "local.set");
693 Result
TypeChecker::OnLocalTee(Type type
) {
694 Result result
= Result::Ok
;
695 result
|= PopAndCheck1Type(type
, "local.tee");
700 Result
TypeChecker::OnLoop(const TypeVector
& param_types
,
701 const TypeVector
& result_types
) {
702 Result result
= PopAndCheckSignature(param_types
, "loop");
703 PushLabel(LabelType::Loop
, param_types
, result_types
);
704 PushTypes(param_types
);
708 Result
TypeChecker::OnMemoryCopy(const Limits
& src_limits
,
709 const Limits
& dst_limits
) {
710 Limits size_limits
= src_limits
;
711 // The memory64 proposal specifies that the type of the size argument should
712 // be the mimimum of the two memory types.
713 if (src_limits
.is_64
&& !dst_limits
.is_64
) {
714 size_limits
= dst_limits
;
716 return CheckOpcode3(Opcode::MemoryCopy
, &src_limits
, &dst_limits
,
720 Result
TypeChecker::OnDataDrop(uint32_t segment
) {
724 Result
TypeChecker::OnMemoryFill(const Limits
& limits
) {
725 return CheckOpcode3(Opcode::MemoryFill
, &limits
, nullptr, &limits
);
728 Result
TypeChecker::OnMemoryGrow(const Limits
& limits
) {
729 Result result
= PopAndCheck1Type(limits
.IndexType(), "memory.grow");
730 PushType(limits
.IndexType());
734 Result
TypeChecker::OnMemoryInit(uint32_t segment
, const Limits
& limits
) {
735 return CheckOpcode3(Opcode::MemoryInit
, &limits
);
738 Result
TypeChecker::OnMemorySize(const Limits
& limits
) {
739 PushType(limits
.IndexType());
743 Result
TypeChecker::OnTableCopy() {
744 return CheckOpcode3(Opcode::TableCopy
);
747 Result
TypeChecker::OnElemDrop(uint32_t segment
) {
751 Result
TypeChecker::OnTableInit(uint32_t table
, uint32_t segment
) {
752 return CheckOpcode3(Opcode::TableInit
);
755 Result
TypeChecker::OnTableGet(Type elem_type
) {
756 Result result
= PopAndCheck1Type(Type::I32
, "table.get");
761 Result
TypeChecker::OnTableSet(Type elem_type
) {
762 return PopAndCheck2Types(Type::I32
, elem_type
, "table.set");
765 Result
TypeChecker::OnTableGrow(Type elem_type
) {
766 Result result
= PopAndCheck2Types(elem_type
, Type::I32
, "table.grow");
771 Result
TypeChecker::OnTableSize() {
776 Result
TypeChecker::OnTableFill(Type elem_type
) {
777 return PopAndCheck3Types(Type::I32
, elem_type
, Type::I32
, "table.fill");
780 Result
TypeChecker::OnRefFuncExpr(Index func_type
) {
781 if (features_
.function_references_enabled()) {
782 PushType(Type(Type::Reference
, func_type
));
784 PushType(Type::FuncRef
);
789 Result
TypeChecker::OnRefNullExpr(Type type
) {
794 Result
TypeChecker::OnRefIsNullExpr() {
796 Result result
= PeekType(0, &type
);
797 if (!(type
== Type::Any
|| type
.IsRef())) {
799 if (Succeeded(result
)) {
800 actual
.push_back(type
);
802 std::string message
=
803 "type mismatch in ref.is_null, expected reference but got " +
804 TypesToString(actual
);
805 PrintError("%s", message
.c_str());
806 result
= Result::Error
;
808 result
|= DropTypes(1);
813 Result
TypeChecker::OnRethrow(Index depth
) {
814 Result result
= Result::Ok
;
816 CHECK_RESULT(GetRethrowLabel(depth
, &label
));
817 CHECK_RESULT(SetUnreachable());
821 Result
TypeChecker::OnThrow(const TypeVector
& sig
) {
822 Result result
= Result::Ok
;
823 result
|= PopAndCheckSignature(sig
, "throw");
824 CHECK_RESULT(SetUnreachable());
828 Result
TypeChecker::OnReturn() {
829 Result result
= Result::Ok
;
831 CHECK_RESULT(GetThisFunctionLabel(&func_label
));
832 result
|= PopAndCheckSignature(func_label
->result_types
, "return");
833 CHECK_RESULT(SetUnreachable());
837 Result
TypeChecker::OnSelect(const TypeVector
& expected
) {
838 Result result
= Result::Ok
;
839 Type type1
= Type::Any
;
840 Type type2
= Type::Any
;
841 Type result_type
= Type::Any
;
842 result
|= PeekAndCheckType(0, Type::I32
);
843 result
|= PeekType(1, &type1
);
844 result
|= PeekType(2, &type2
);
845 if (expected
.empty()) {
846 if (type1
.IsRef() || type2
.IsRef()) {
847 result
= Result::Error
;
849 result
|= CheckType(type1
, type2
);
853 assert(expected
.size() == 1);
854 result
|= CheckType(type1
, expected
[0]);
855 result
|= CheckType(type2
, expected
[0]);
857 PrintStackIfFailed(result
, "select", result_type
, result_type
, Type::I32
);
858 result
|= DropTypes(3);
859 PushType(result_type
);
863 Result
TypeChecker::OnStore(Opcode opcode
, const Limits
& limits
) {
864 return CheckOpcode2(opcode
, &limits
);
867 Result
TypeChecker::OnTry(const TypeVector
& param_types
,
868 const TypeVector
& result_types
) {
869 Result result
= PopAndCheckSignature(param_types
, "try");
870 PushLabel(LabelType::Try
, param_types
, result_types
);
871 PushTypes(param_types
);
875 Result
TypeChecker::OnUnary(Opcode opcode
) {
876 return CheckOpcode1(opcode
);
879 Result
TypeChecker::OnTernary(Opcode opcode
) {
880 return CheckOpcode3(opcode
);
883 Result
TypeChecker::OnSimdLaneOp(Opcode opcode
, uint64_t lane_idx
) {
884 Result result
= Result::Ok
;
885 uint32_t lane_count
= opcode
.GetSimdLaneCount();
886 if (lane_idx
>= lane_count
) {
887 PrintError("lane index must be less than %d (got %" PRIu64
")", lane_count
,
889 result
= Result::Error
;
893 case Opcode::I8X16ExtractLaneS
:
894 case Opcode::I8X16ExtractLaneU
:
895 case Opcode::I16X8ExtractLaneS
:
896 case Opcode::I16X8ExtractLaneU
:
897 case Opcode::I32X4ExtractLane
:
898 case Opcode::F32X4ExtractLane
:
899 case Opcode::I64X2ExtractLane
:
900 case Opcode::F64X2ExtractLane
:
901 result
|= CheckOpcode1(opcode
);
903 case Opcode::I8X16ReplaceLane
:
904 case Opcode::I16X8ReplaceLane
:
905 case Opcode::I32X4ReplaceLane
:
906 case Opcode::F32X4ReplaceLane
:
907 case Opcode::I64X2ReplaceLane
:
908 case Opcode::F64X2ReplaceLane
:
909 result
|= CheckOpcode2(opcode
);
917 Result
TypeChecker::OnSimdLoadLane(Opcode opcode
,
918 const Limits
& limits
,
920 Result result
= Result::Ok
;
921 uint32_t lane_count
= opcode
.GetSimdLaneCount();
922 if (lane_idx
>= lane_count
) {
923 PrintError("lane index must be less than %d (got %" PRIu64
")", lane_count
,
925 result
= Result::Error
;
927 result
|= CheckOpcode2(opcode
, &limits
);
931 Result
TypeChecker::OnSimdStoreLane(Opcode opcode
,
932 const Limits
& limits
,
934 Result result
= Result::Ok
;
935 uint32_t lane_count
= opcode
.GetSimdLaneCount();
936 if (lane_idx
>= lane_count
) {
937 PrintError("lane index must be less than %d (got %" PRIu64
")", lane_count
,
939 result
= Result::Error
;
941 result
|= CheckOpcode2(opcode
, &limits
);
945 Result
TypeChecker::OnSimdShuffleOp(Opcode opcode
, v128 lane_idx
) {
946 Result result
= Result::Ok
;
947 uint8_t simd_data
[16];
948 memcpy(simd_data
, &lane_idx
, 16);
949 for (int i
= 0; i
< 16; i
++) {
950 if (simd_data
[i
] >= 32) {
951 PrintError("lane index must be less than 32 (got %d)", simd_data
[i
]);
952 result
= Result::Error
;
956 result
|= CheckOpcode2(opcode
);
960 Result
TypeChecker::OnUnreachable() {
961 return SetUnreachable();
964 Result
TypeChecker::EndFunction() {
965 Result result
= Result::Ok
;
967 CHECK_RESULT(TopLabel(&label
));
968 result
|= CheckLabelType(label
, LabelType::Func
);
969 result
|= OnEnd(label
, "implicit return", "function");
973 Result
TypeChecker::BeginInitExpr(Type type
) {
975 label_stack_
.clear();
976 PushLabel(LabelType::InitExpr
, TypeVector(), {type
});
980 Result
TypeChecker::EndInitExpr() {
981 Result result
= Result::Ok
;
983 CHECK_RESULT(TopLabel(&label
));
984 result
|= CheckLabelType(label
, LabelType::InitExpr
);
985 result
|= OnEnd(label
, "initializer expression", "initializer expression");