Backed out 2 changesets (bug 903746) for causing non-unified build bustages on nsIPri...
[gecko.git] / third_party / wasm2c / src / type-checker.cc
blob79b4f60496c044c446bebe899cab02be17fee551
1 /*
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"
19 #include <cinttypes>
21 namespace wabt {
23 namespace {
25 std::string TypesToString(const TypeVector& types,
26 const char* prefix = nullptr) {
27 std::string result = "[";
28 if (prefix) {
29 result += prefix;
32 for (size_t i = 0; i < types.size(); ++i) {
33 result += types[i].GetName();
34 if (i < types.size() - 1) {
35 result += ", ";
38 result += "]";
39 return result;
42 } // end anonymous namespace
44 TypeChecker::Label::Label(LabelType label_type,
45 const TypeVector& param_types,
46 const TypeVector& result_types,
47 size_t limit)
48 : label_type(label_type),
49 param_types(param_types),
50 result_types(result_types),
51 type_stack_limit(limit),
52 unreachable(false) {}
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);
66 *out_label = nullptr;
67 return Result::Error;
69 *out_label = &label_stack_[label_stack_.size() - depth - 1];
70 return Result::Ok;
73 Result TypeChecker::GetRethrowLabel(Index depth, Label** out_label) {
74 if (Failed(GetLabel(depth, out_label))) {
75 return Result::Error;
78 if ((*out_label)->label_type == LabelType::Catch) {
79 return Result::Ok;
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");
95 } else {
96 PrintError("invalid rethrow depth: %" PRIindex " (catches: %s)", depth,
97 candidates.c_str());
99 *out_label = nullptr;
100 return Result::Error;
103 Result TypeChecker::GetCatchCount(Index depth, Index* out_count) {
104 Label* unused;
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) {
113 catch_count++;
116 *out_count = catch_count;
118 return Result::Ok;
121 Result TypeChecker::TopLabel(Label** out_label) {
122 return GetLabel(0, out_label);
125 bool TypeChecker::IsUnreachable() {
126 Label* label;
127 if (Failed(TopLabel(&label))) {
128 return true;
130 return label->unreachable;
133 void TypeChecker::ResetTypeStackToLabel(Label* label) {
134 type_stack_.resize(label->type_stack_limit);
137 Result TypeChecker::SetUnreachable() {
138 Label* label;
139 CHECK_RESULT(TopLabel(&label));
140 label->unreachable = true;
141 ResetTypeStackToLabel(label);
142 return Result::Ok;
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,
149 type_stack_.size());
152 Result TypeChecker::PopLabel() {
153 label_stack_.pop_back();
154 return Result::Ok;
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
165 ? Result::Ok
166 : Result::Error;
169 Result TypeChecker::GetThisFunctionLabel(Label** label) {
170 return GetLabel(label_stack_.size() - 1, label);
173 Result TypeChecker::PeekType(Index depth, Type* out_type) {
174 Label* label;
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];
182 return Result::Ok;
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) {
192 Label* label;
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());
199 return Result::Ok;
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) {
210 PushType(type);
214 Result TypeChecker::CheckTypeStackEnd(const char* desc) {
215 Label* label;
216 CHECK_RESULT(TopLabel(&label));
217 Result result = (type_stack_.size() == label->type_stack_limit)
218 ? Result::Ok
219 : Result::Error;
220 PrintStackIfFailedV(result, desc, {}, /*is_end=*/true);
221 return result;
224 Result TypeChecker::CheckType(Type actual, Type expected) {
225 if (expected == Type::Any || actual == Type::Any) {
226 return Result::Ok;
229 if (expected == Type::Reference && actual == Type::Reference) {
230 return expected.GetReferenceIndex() == actual.GetReferenceIndex()
231 ? Result::Ok
232 : Result::Error;
234 if (actual != expected) {
235 return Result::Error;
237 return Result::Ok;
240 Result TypeChecker::CheckTypes(const TypeVector& actual,
241 const TypeVector& expected) {
242 if (actual.size() != expected.size()) {
243 return Result::Error;
244 } else {
245 Result result = Result::Ok;
246 for (size_t i = 0; i < actual.size(); i++)
247 result |= CheckType(actual[i], expected[i]);
248 return result;
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);
258 return result;
261 Result TypeChecker::CheckReturnSignature(const TypeVector& actual,
262 const TypeVector& expected,
263 const char* desc) {
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());
269 return result;
272 Result TypeChecker::PopAndCheckSignature(const TypeVector& sig,
273 const char* desc) {
274 Result result = CheckSignature(sig, desc);
275 result |= DropTypes(sig.size());
276 return result;
279 Result TypeChecker::PopAndCheckCall(const TypeVector& param_types,
280 const TypeVector& result_types,
281 const char* desc) {
282 Result result = CheckSignature(param_types, desc);
283 result |= DropTypes(param_types.size());
284 PushTypes(result_types);
285 return result;
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);
293 return result;
296 Result TypeChecker::PopAndCheck2Types(Type expected1,
297 Type expected2,
298 const char* desc) {
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);
304 return result;
307 Result TypeChecker::PopAndCheck3Types(Type expected1,
308 Type expected2,
309 Type expected3,
310 const char* desc) {
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);
317 return result;
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());
330 return result;
333 Result TypeChecker::CheckOpcode2(Opcode opcode, const Limits* limits) {
334 Result result =
335 PopAndCheck2Types(GetMemoryParam(opcode.GetParamType1(), limits),
336 opcode.GetParamType2(), opcode.GetName());
337 PushType(opcode.GetResultType());
338 return result;
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());
350 return result;
353 void TypeChecker::PrintStackIfFailedV(Result result,
354 const char* desc,
355 const TypeVector& expected,
356 bool is_end) {
357 if (Succeeded(result)) {
358 return;
361 size_t limit = 0;
362 Label* label;
363 if (Succeeded(TopLabel(&label))) {
364 limit = label->type_stack_limit;
367 TypeVector actual;
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.
373 size_t actual_size;
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);
378 } else {
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) {
385 Type type;
386 Result result = PeekType(actual_size - i - 1, &type);
387 WABT_USE(result);
388 assert(Succeeded(result));
389 actual.push_back(type);
392 std::string message = "type mismatch in ";
393 if (is_end) {
394 message = "type mismatch at end of ";
396 message += desc;
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) {
406 type_stack_.clear();
407 label_stack_.clear();
408 PushLabel(LabelType::Func, TypeVector(), sig);
409 return Result::Ok;
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) {
433 return Result::Ok;
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);
449 return result;
452 Result TypeChecker::OnBr(Index depth) {
453 Result result = Result::Ok;
454 Label* label;
455 CHECK_RESULT(GetLabel(depth, &label));
456 result |= CheckSignature(label->br_types(), "br");
457 CHECK_RESULT(SetUnreachable());
458 return result;
461 Result TypeChecker::OnBrIf(Index depth) {
462 Result result = PopAndCheck1Type(Type::I32, "br_if");
463 Label* label;
464 CHECK_RESULT(GetLabel(depth, &label));
465 result |= PopAndCheckSignature(label->br_types(), "br_if");
466 PushTypes(label->br_types());
467 return result;
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;
477 Label* label;
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'
483 // signatures.
484 if (br_table_sig_ == nullptr) {
485 br_table_sig_ = &label_sig;
486 } else {
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());
495 return result;
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");
511 return result;
514 Result TypeChecker::OnIndexedFuncRef(Index* out_index) {
515 Type type;
516 CHECK_RESULT(PeekType(0, &type));
517 Result result = Result::Ok;
518 if (!(type == Type::Any || type.IsReferenceWithIndex())) {
519 TypeVector actual;
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);
531 return result;
534 Result TypeChecker::OnReturnCall(const TypeVector& param_types,
535 const TypeVector& result_types) {
536 Result result = PopAndCheckSignature(param_types, "return_call");
537 Label* func_label;
538 CHECK_RESULT(GetThisFunctionLabel(&func_label));
539 result |= CheckReturnSignature(result_types, func_label->result_types,
540 "return_call");
542 CHECK_RESULT(SetUnreachable());
543 return result;
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");
551 Label* func_label;
552 CHECK_RESULT(GetThisFunctionLabel(&func_label));
553 result |= CheckReturnSignature(result_types, func_label->result_types,
554 "return_call_indirect");
556 CHECK_RESULT(SetUnreachable());
557 return result;
560 Result TypeChecker::OnCompare(Opcode opcode) {
561 return CheckOpcode2(opcode);
564 Result TypeChecker::OnCatch(const TypeVector& sig) {
565 Result result = Result::Ok;
566 Label* label;
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;
574 PushTypes(sig);
575 return result;
578 Result TypeChecker::OnConst(Type type) {
579 PushType(type);
580 return Result::Ok;
583 Result TypeChecker::OnConvert(Opcode opcode) {
584 return CheckOpcode1(opcode);
587 Result TypeChecker::OnDelegate(Index depth) {
588 Result result = Result::Ok;
589 Label* label;
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));
594 Label* try_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);
604 PopLabel();
605 return result;
608 Result TypeChecker::OnDrop() {
609 Result result = Result::Ok;
610 result |= DropTypes(1);
611 PrintStackIfFailed(result, "drop", Type::Any);
612 return result;
615 Result TypeChecker::OnElse() {
616 Result result = Result::Ok;
617 Label* label;
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;
626 return result;
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);
637 PopLabel();
638 return result;
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);
647 Label* label;
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);
659 return result;
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);
668 return result;
671 Result TypeChecker::OnGlobalGet(Type type) {
672 PushType(type);
673 return Result::Ok;
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) {
685 PushType(type);
686 return Result::Ok;
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");
696 PushType(type);
697 return result;
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);
705 return result;
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,
717 &size_limits);
720 Result TypeChecker::OnDataDrop(uint32_t segment) {
721 return Result::Ok;
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());
731 return result;
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());
740 return Result::Ok;
743 Result TypeChecker::OnTableCopy() {
744 return CheckOpcode3(Opcode::TableCopy);
747 Result TypeChecker::OnElemDrop(uint32_t segment) {
748 return Result::Ok;
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");
757 PushType(elem_type);
758 return result;
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");
767 PushType(Type::I32);
768 return result;
771 Result TypeChecker::OnTableSize() {
772 PushType(Type::I32);
773 return Result::Ok;
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));
783 } else {
784 PushType(Type::FuncRef);
786 return Result::Ok;
789 Result TypeChecker::OnRefNullExpr(Type type) {
790 PushType(type);
791 return Result::Ok;
794 Result TypeChecker::OnRefIsNullExpr() {
795 Type type;
796 Result result = PeekType(0, &type);
797 if (!(type == Type::Any || type.IsRef())) {
798 TypeVector actual;
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);
809 PushType(Type::I32);
810 return result;
813 Result TypeChecker::OnRethrow(Index depth) {
814 Result result = Result::Ok;
815 Label* label;
816 CHECK_RESULT(GetRethrowLabel(depth, &label));
817 CHECK_RESULT(SetUnreachable());
818 return result;
821 Result TypeChecker::OnThrow(const TypeVector& sig) {
822 Result result = Result::Ok;
823 result |= PopAndCheckSignature(sig, "throw");
824 CHECK_RESULT(SetUnreachable());
825 return result;
828 Result TypeChecker::OnReturn() {
829 Result result = Result::Ok;
830 Label* func_label;
831 CHECK_RESULT(GetThisFunctionLabel(&func_label));
832 result |= PopAndCheckSignature(func_label->result_types, "return");
833 CHECK_RESULT(SetUnreachable());
834 return result;
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;
848 } else {
849 result |= CheckType(type1, type2);
850 result_type = type1;
852 } else {
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);
860 return result;
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);
872 return result;
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,
888 lane_idx);
889 result = Result::Error;
892 switch (opcode) {
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);
902 break;
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);
910 break;
911 default:
912 WABT_UNREACHABLE;
914 return result;
917 Result TypeChecker::OnSimdLoadLane(Opcode opcode,
918 const Limits& limits,
919 uint64_t lane_idx) {
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,
924 lane_idx);
925 result = Result::Error;
927 result |= CheckOpcode2(opcode, &limits);
928 return result;
931 Result TypeChecker::OnSimdStoreLane(Opcode opcode,
932 const Limits& limits,
933 uint64_t lane_idx) {
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,
938 lane_idx);
939 result = Result::Error;
941 result |= CheckOpcode2(opcode, &limits);
942 return result;
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);
957 return result;
960 Result TypeChecker::OnUnreachable() {
961 return SetUnreachable();
964 Result TypeChecker::EndFunction() {
965 Result result = Result::Ok;
966 Label* label;
967 CHECK_RESULT(TopLabel(&label));
968 result |= CheckLabelType(label, LabelType::Func);
969 result |= OnEnd(label, "implicit return", "function");
970 return result;
973 Result TypeChecker::BeginInitExpr(Type type) {
974 type_stack_.clear();
975 label_stack_.clear();
976 PushLabel(LabelType::InitExpr, TypeVector(), {type});
977 return Result::Ok;
980 Result TypeChecker::EndInitExpr() {
981 Result result = Result::Ok;
982 Label* label;
983 CHECK_RESULT(TopLabel(&label));
984 result |= CheckLabelType(label, LabelType::InitExpr);
985 result |= OnEnd(label, "initializer expression", "initializer expression");
986 return result;
989 } // namespace wabt