diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index e143c259f22b..14d346a7c0ee 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -28,6 +28,7 @@ #include "explorer/interpreter/action_stack.h" #include "explorer/interpreter/stack.h" #include "explorer/interpreter/stack_fragment.h" +#include "llvm/ADT/APInt.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" @@ -210,23 +211,34 @@ auto Interpreter::EvalPrim(Operator op, Nonnull /*static_type*/, -> ErrorOr> { switch (op) { case Operator::Neg: - return arena_->New(-cast(*args[0]).value()); case Operator::Add: - return arena_->New(cast(*args[0]).value() + - cast(*args[1]).value()); case Operator::Sub: - return arena_->New(cast(*args[0]).value() - - cast(*args[1]).value()); - case Operator::Mul: - return arena_->New(cast(*args[0]).value() * - cast(*args[1]).value()); - case Operator::Div: { - const auto& lhs = cast(*args[0]).value(); - const auto& rhs = cast(*args[1]).value(); - if (rhs == 0) { - return ProgramError(source_loc) << "division by zero"; + case Operator::Div: + case Operator::Mul: { + llvm::APInt op0(64, cast(*args[0]).value()); + llvm::APInt result; + if (op == Operator::Neg) { + result = -op0; + } else { + llvm::APInt op1(64, cast(*args[1]).value()); + if (op == Operator::Add) { + result = op0 + op1; + } else if (op == Operator::Sub) { + result = op0 - op1; + } else if (op == Operator::Mul) { + result = op0 * op1; + } else if (op == Operator::Div) { + if (op1.getSExtValue() == 0) { + return ProgramError(source_loc) << "division by zero"; + } + result = op0.sdiv(op1); + } + } + if (result.isSignedIntN(32)) { + return arena_->New(result.getSExtValue()); + } else { + return ProgramError(source_loc) << "integer overflow"; } - return arena_->New(lhs / rhs); } case Operator::Mod: { const auto& lhs = cast(*args[0]).value(); diff --git a/explorer/testdata/operators/fail_overflow_add.carbon b/explorer/testdata/operators/fail_overflow_add.carbon new file mode 100644 index 000000000000..b02f636a248c --- /dev/null +++ b/explorer/testdata/operators/fail_overflow_add.carbon @@ -0,0 +1,15 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +fn Main() -> i32 { + // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/testdata/operators/fail_overflow_add.carbon:[[@LINE+1]]: integer overflow + var a: auto = 2147483647 + 1; + return 0; +} diff --git a/explorer/testdata/operators/fail_overflow_div.carbon b/explorer/testdata/operators/fail_overflow_div.carbon new file mode 100644 index 000000000000..a17bba3c9aa9 --- /dev/null +++ b/explorer/testdata/operators/fail_overflow_div.carbon @@ -0,0 +1,15 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +fn Main() -> i32 { + // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/testdata/operators/fail_overflow_div.carbon:[[@LINE+1]]: integer overflow + var a: auto = (-2147483647 -1) /-1; + return 0; +} diff --git a/explorer/testdata/operators/fail_overflow_multiply.carbon b/explorer/testdata/operators/fail_overflow_multiply.carbon new file mode 100644 index 000000000000..6cba1ee1e8e5 --- /dev/null +++ b/explorer/testdata/operators/fail_overflow_multiply.carbon @@ -0,0 +1,15 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +fn Main() -> i32 { + // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/testdata/operators/fail_overflow_multiply.carbon:[[@LINE+1]]: integer overflow + var a: auto = 1000000000 * 1000000000; + return 0; +} diff --git a/explorer/testdata/operators/fail_overflow_sub.carbon b/explorer/testdata/operators/fail_overflow_sub.carbon new file mode 100644 index 000000000000..be3ce2d6772b --- /dev/null +++ b/explorer/testdata/operators/fail_overflow_sub.carbon @@ -0,0 +1,15 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +fn Main() -> i32 { + // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/testdata/operators/fail_overflow_sub.carbon:[[@LINE+1]]: integer overflow + var a: auto = -2147483647 - 2; + return 0; +}