diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/type-checker.cc | 204 | ||||
-rw-r--r-- | src/type-checker.h | 19 |
2 files changed, 123 insertions, 100 deletions
diff --git a/src/type-checker.cc b/src/type-checker.cc index 87650cf6..b7c76323 100644 --- a/src/type-checker.cc +++ b/src/type-checker.cc @@ -96,17 +96,10 @@ Result TypeChecker::PeekType(Index depth, Type* out_type) { return Result::Ok; } -Result TypeChecker::TopType(Type* out_type) { - return PeekType(0, out_type); -} - -Result TypeChecker::PopType(Type* out_type) { - Label* label; - CHECK_RESULT(TopLabel(&label)); - Result result = TopType(out_type); - if (type_stack_.size() > label->type_stack_limit) - type_stack_.pop_back(); - return result; +Result TypeChecker::PeekAndCheckType(Index depth, Type expected) { + Type actual = Type::Any; + Result result = PeekType(depth, &actual); + return result | CheckType(actual, expected); } Result TypeChecker::DropTypes(size_t drop_count) { @@ -133,52 +126,33 @@ void TypeChecker::PushTypes(const TypeVector& types) { PushType(type); } -Result TypeChecker::CheckTypeStackLimit(size_t expected, const char* desc) { - Label* label; - CHECK_RESULT(TopLabel(&label)); - size_t avail = type_stack_.size() - label->type_stack_limit; - if (!label->unreachable && expected > avail) { - PrintError("type stack size too small at %s. got %" PRIzd - ", expected at least %" PRIzd, - desc, avail, expected); - return Result::Error; - } - return Result::Ok; -} - Result TypeChecker::CheckTypeStackEnd(const char* desc) { Label* label; CHECK_RESULT(TopLabel(&label)); - if (type_stack_.size() != label->type_stack_limit) { - PrintError("type stack at end of %s is %" PRIzd ", expected %" PRIzd, desc, - type_stack_.size(), label->type_stack_limit); - return Result::Error; - } - return Result::Ok; + Result result = (type_stack_.size() == label->type_stack_limit) + ? Result::Ok + : Result::Error; + PrintStackIfFailed(result, desc); + return result; } -Result TypeChecker::CheckType(Type actual, Type expected, const char* desc) { - if (expected != actual && expected != Type::Any && actual != Type::Any) { - PrintError("type mismatch in %s, expected %s but got %s.", desc, - GetTypeName(expected), GetTypeName(actual)); - return Result::Error; - } - return Result::Ok; +Result TypeChecker::CheckType(Type actual, Type expected) { + return (expected == actual || expected == Type::Any || actual == Type::Any) + ? Result::Ok + : Result::Error; } -Result TypeChecker::CheckSignature(const TypeVector& sig, const char* desc) { - Result result = CheckTypeStackLimit(sig.size(), desc); - for (size_t i = 0; i < sig.size(); ++i) { - Type actual = Type::Any; - result |= PeekType(sig.size() - i - 1, &actual); - result |= CheckType(actual, sig[i], desc); - } +Result TypeChecker::CheckSignature(const TypeVector& sig) { + Result result = Result::Ok; + for (size_t i = 0; i < sig.size(); ++i) + result |= PeekAndCheckType(sig.size() - i - 1, sig[i]); return result; } Result TypeChecker::PopAndCheckSignature(const TypeVector& sig, const char* desc) { - Result result = CheckSignature(sig, desc); + Result result = CheckSignature(sig); + PrintStackIfFailed(result, desc, sig); result |= DropTypes(sig.size()); return result; } @@ -186,12 +160,8 @@ Result TypeChecker::PopAndCheckSignature(const TypeVector& sig, Result TypeChecker::PopAndCheckCall(const TypeVector& param_types, const TypeVector& result_types, const char* desc) { - Result result = CheckTypeStackLimit(param_types.size(), desc); - for (size_t i = 0; i < param_types.size(); ++i) { - Type actual = Type::Any; - result |= PeekType(param_types.size() - i - 1, &actual); - result |= CheckType(actual, param_types[i], desc); - } + Result result = CheckSignature(param_types); + PrintStackIfFailed(result, desc, param_types); result |= DropTypes(param_types.size()); PushTypes(result_types); return result; @@ -199,10 +169,9 @@ Result TypeChecker::PopAndCheckCall(const TypeVector& param_types, Result TypeChecker::PopAndCheck1Type(Type expected, const char* desc) { Result result = Result::Ok; - Type actual = Type::Any; - result |= CheckTypeStackLimit(1, desc); - result |= PopType(&actual); - result |= CheckType(actual, expected, desc); + result |= PeekAndCheckType(0, expected); + PrintStackIfFailed(result, desc, expected); + result |= DropTypes(1); return result; } @@ -210,13 +179,10 @@ Result TypeChecker::PopAndCheck2Types(Type expected1, Type expected2, const char* desc) { Result result = Result::Ok; - Type actual1 = Type::Any; - Type actual2 = Type::Any; - result |= CheckTypeStackLimit(2, desc); - result |= PopType(&actual2); - result |= PopType(&actual1); - result |= CheckType(actual1, expected1, desc); - result |= CheckType(actual2, expected2, desc); + result |= PeekAndCheckType(0, expected2); + result |= PeekAndCheckType(1, expected1); + PrintStackIfFailed(result, desc, expected1, expected2); + result |= DropTypes(2); return result; } @@ -225,29 +191,11 @@ Result TypeChecker::PopAndCheck3Types(Type expected1, Type expected3, const char* desc) { Result result = Result::Ok; - Type actual1 = Type::Any; - Type actual2 = Type::Any; - Type actual3 = Type::Any; - result |= CheckTypeStackLimit(3, desc); - result |= PopType(&actual3); - result |= PopType(&actual2); - result |= PopType(&actual1); - result |= CheckType(actual1, expected1, desc); - result |= CheckType(actual2, expected2, desc); - result |= CheckType(actual3, expected3, desc); - return result; -} - -Result TypeChecker::PopAndCheck2TypesAreEqual(Type* out_type, - const char* desc) { - Result result = Result::Ok; - Type right = Type::Any; - Type left = Type::Any; - result |= CheckTypeStackLimit(2, desc); - result |= PopType(&right); - result |= PopType(&left); - result |= CheckType(left, right, desc); - *out_type = right; + result |= PeekAndCheckType(0, expected3); + result |= PeekAndCheckType(1, expected2); + result |= PeekAndCheckType(2, expected1); + PrintStackIfFailed(result, desc, expected1, expected2, expected3); + result |= DropTypes(3); return result; } @@ -272,6 +220,68 @@ Result TypeChecker::CheckOpcode3(Opcode opcode) { return result; } +static std::string TypesToString(const TypeVector& types, + const char* prefix = nullptr) { + std::string result = "["; + if (prefix) + result += prefix; + + for (size_t i = 0; i < types.size(); ++i) { + result += GetTypeName(types[i]); + if (i < types.size() - 1) + result += ", "; + } + result += "]"; + return result; +} + +void TypeChecker::PrintStackIfFailed(Result result, + const char* desc, + const TypeVector& expected) { + if (Failed(result)) { + size_t limit = 0; + Label* label; + if (Succeeded(TopLabel(&label))) { + limit = label->type_stack_limit; + } + + TypeVector actual; + size_t max_depth = type_stack_.size() - limit; + + // In general we want to print as many values of the actual stack as were + // expected. However, if the stack was expected to be empty, we should + // print some amount of the actual stack. + size_t actual_size; + if (expected.size() == 0) { + // Don't print too many elements if the stack is really deep. + const size_t kMaxActualStackToPrint = 4; + actual_size = std::min(kMaxActualStackToPrint, max_depth); + } else { + actual_size = std::min(expected.size(), max_depth); + } + + bool incomplete_actual_stack = actual_size != max_depth; + + for (size_t i = 0; i < actual_size; ++i) { + Type type; + Result result = PeekType(actual_size - i - 1, &type); + WABT_USE(result); + assert(Succeeded(result)); + actual.push_back(type); + } + + std::string message = "type mismatch in "; + message += desc; + message += ", expected "; + message += TypesToString(expected); + message += " but got "; + message += + TypesToString(actual, incomplete_actual_stack ? "... " : nullptr); + + PrintError("%s", message.c_str()); + } +} + Result TypeChecker::BeginFunction(const TypeVector* sig) { type_stack_.clear(); label_stack_.clear(); @@ -309,7 +319,8 @@ Result TypeChecker::OnBr(Index depth) { Label* label; CHECK_RESULT(GetLabel(depth, &label)); if (label->label_type != LabelType::Loop) - result |= CheckSignature(label->sig, "br"); + result |= CheckSignature(label->sig); + PrintStackIfFailed(result, "br", label->sig); CHECK_RESULT(SetUnreachable()); return result; } @@ -340,13 +351,16 @@ Result TypeChecker::OnBrTableTarget(Index depth) { } else { assert(label->sig.size() <= 1); label_sig = label->sig.size() == 0 ? Type::Void : label->sig[0]; + + result |= CheckSignature(label->sig); + PrintStackIfFailed(result, "br_table", label_sig); } - result |= CheckType(br_table_sig_, label_sig, "br_table"); + // Make sure this label's signature is consistent with the previous labels' + // signatures. + result |= CheckType(br_table_sig_, label_sig); br_table_sig_ = label_sig; - if (label->label_type != LabelType::Loop) - result |= CheckSignature(label->sig, "br_table"); return result; } @@ -404,9 +418,8 @@ Result TypeChecker::OnCurrentMemory() { Result TypeChecker::OnDrop() { Result result = Result::Ok; - Type type = Type::Any; - result |= CheckTypeStackLimit(1, "drop"); - result |= PopType(&type); + result |= DropTypes(1); + PrintStackIfFailed(result, "drop", Type::Any); return result; } @@ -528,9 +541,12 @@ Result TypeChecker::OnReturn() { Result TypeChecker::OnSelect() { Result result = Result::Ok; - result |= PopAndCheck1Type(Type::I32, "select"); Type type = Type::Any; - result |= PopAndCheck2TypesAreEqual(&type, "select"); + result |= PeekAndCheckType(0, Type::I32); + result |= PeekType(1, &type); + result |= PeekAndCheckType(2, type); + PrintStackIfFailed(result, "select", Type::I32, type, type); + result |= DropTypes(3); PushType(type); return result; } diff --git a/src/type-checker.h b/src/type-checker.h index 1176f95d..b2e32045 100644 --- a/src/type-checker.h +++ b/src/type-checker.h @@ -104,15 +104,13 @@ class TypeChecker { Result PopLabel(); Result CheckLabelType(Label* label, LabelType label_type); Result PeekType(Index depth, Type* out_type); - Result TopType(Type* out_type); - Result PopType(Type* out_type); + Result PeekAndCheckType(Index depth, Type expected); Result DropTypes(size_t drop_count); void PushType(Type type); void PushTypes(const TypeVector& types); - Result CheckTypeStackLimit(size_t expected, const char* desc); Result CheckTypeStackEnd(const char* desc); - Result CheckType(Type actual, Type expected, const char* desc); - Result CheckSignature(const TypeVector& sig, const char* desc); + Result CheckType(Type actual, Type expected); + Result CheckSignature(const TypeVector& sig); Result PopAndCheckSignature(const TypeVector& sig, const char* desc); Result PopAndCheckCall(const TypeVector& param_types, const TypeVector& result_types, @@ -123,12 +121,21 @@ class TypeChecker { Type expected2, Type expected3, const char* desc); - Result PopAndCheck2TypesAreEqual(Type* out_type, const char* desc); Result CheckOpcode1(Opcode opcode); Result CheckOpcode2(Opcode opcode); Result CheckOpcode3(Opcode opcode); Result OnEnd(Label* label, const char* sig_desc, const char* end_desc); + template <typename... Args> + void PrintStackIfFailed(Result result, const char* desc, Args... args) { + // Minor optimzation, check result before constructing the vector to pass + // to the other overload of PrintStackIfFailed. + if (Failed(result)) + PrintStackIfFailed(result, desc, {args...}); + } + + void PrintStackIfFailed(Result, const char* desc, const TypeVector&); + ErrorCallback error_callback_; TypeVector type_stack_; std::vector<Label> label_stack_; |