From 69d4363ea5789b0bb30a7e3aaa702139a09f0f93 Mon Sep 17 00:00:00 2001 From: Junhee Cho <1294603+junheecho@users.noreply.github.com> Date: Tue, 20 Sep 2022 07:02:29 +0900 Subject: [PATCH] Adds division to multiplicative expression. (#2091) * Multiplication and division have the same priority. * A new builtin interface DivWith is added. * In some tests expecting a compilation error (syntax error), the error message now says it is expecting SLASH or binary *. --- common/fuzzing/carbon.proto | 1 + common/fuzzing/proto_to_carbon.cpp | 4 ++++ explorer/ast/expression.cpp | 2 ++ explorer/ast/expression.h | 1 + explorer/data/prelude.carbon | 10 ++++++++ explorer/fuzzing/ast_to_proto.cpp | 2 ++ explorer/interpreter/builtins.h | 12 ++++++---- explorer/interpreter/interpreter.cpp | 3 +++ explorer/interpreter/type_checker.cpp | 2 ++ explorer/syntax/parser.ypp | 6 +++++ .../basic_syntax/fail_missing_var.carbon | 2 +- .../basic_syntax/fail_var_named_self.carbon | 2 +- .../testdata/let/fail_local_named_self.carbon | 2 +- explorer/testdata/operators/div.carbon | 24 +++++++++++++++++++ .../testdata/operators/div_builtin.carbon | 24 +++++++++++++++++++ 15 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 explorer/testdata/operators/div.carbon create mode 100644 explorer/testdata/operators/div_builtin.carbon diff --git a/common/fuzzing/carbon.proto b/common/fuzzing/carbon.proto index 07ce2d73fd03..9ae4ebaef5a8 100644 --- a/common/fuzzing/carbon.proto +++ b/common/fuzzing/carbon.proto @@ -65,6 +65,7 @@ message OperatorExpression { Greater = 22; GreaterEq = 23; NotEq = 24; + Div = 25; } optional Operator op = 1; repeated Expression arguments = 2; diff --git a/common/fuzzing/proto_to_carbon.cpp b/common/fuzzing/proto_to_carbon.cpp index e71b8d448c27..e7792d12701d 100644 --- a/common/fuzzing/proto_to_carbon.cpp +++ b/common/fuzzing/proto_to_carbon.cpp @@ -117,6 +117,10 @@ static auto OperatorToCarbon(const Fuzzing::OperatorExpression& operator_expr, BinaryOperatorToCarbon(arg0, " * ", arg1, out); break; + case Fuzzing::OperatorExpression::Div: + BinaryOperatorToCarbon(arg0, " / ", arg1, out); + break; + case Fuzzing::OperatorExpression::Mod: BinaryOperatorToCarbon(arg0, " % ", arg1, out); break; diff --git a/explorer/ast/expression.cpp b/explorer/ast/expression.cpp index a16df0c0833b..501055173b4b 100644 --- a/explorer/ast/expression.cpp +++ b/explorer/ast/expression.cpp @@ -123,6 +123,8 @@ auto ToString(Operator op) -> std::string_view { return "<<"; case Operator::BitShiftRight: return ">>"; + case Operator::Div: + return "/"; case Operator::Neg: case Operator::Sub: return "-"; diff --git a/explorer/ast/expression.h b/explorer/ast/expression.h index e92e85cb4a2f..67f923e29b04 100644 --- a/explorer/ast/expression.h +++ b/explorer/ast/expression.h @@ -123,6 +123,7 @@ enum class Operator { BitShiftRight, Complement, Deref, + Div, Eq, Less, LessEq, diff --git a/explorer/data/prelude.carbon b/explorer/data/prelude.carbon index 1a6baaef1775..594794cdf88f 100644 --- a/explorer/data/prelude.carbon +++ b/explorer/data/prelude.carbon @@ -317,6 +317,13 @@ interface MulWith(U:! Type) { } // TODO: constraint Mul { ... } +interface DivWith(U:! Type) { + // TODO: = Self + let Result:! Type; + fn Op[me: Self](other: U) -> Result; +} +// TODO: constraint Div { ... } + interface ModWith(U:! Type) { // TODO: = Self let Result:! Type; @@ -337,6 +344,9 @@ external impl i32 as SubWith(i32) where .Result == i32 { external impl i32 as MulWith(i32) where .Result == i32 { fn Op[me: i32](other: i32) -> i32 { return me * other; } } +external impl i32 as DivWith(i32) where .Result == i32 { + fn Op[me: i32](other: i32) -> i32 { return me / other; } +} external impl i32 as ModWith(i32) where .Result == i32 { fn Op[me: i32](other: i32) -> i32 { return me % other; } } diff --git a/explorer/fuzzing/ast_to_proto.cpp b/explorer/fuzzing/ast_to_proto.cpp index 8b44f4be62df..a454da502fac 100644 --- a/explorer/fuzzing/ast_to_proto.cpp +++ b/explorer/fuzzing/ast_to_proto.cpp @@ -66,6 +66,8 @@ static auto OperatorToProtoEnum(const Operator op) return Fuzzing::OperatorExpression::GreaterEq; case Operator::Mul: return Fuzzing::OperatorExpression::Mul; + case Operator::Div: + return Fuzzing::OperatorExpression::Div; case Operator::Mod: return Fuzzing::OperatorExpression::Mod; case Operator::Or: diff --git a/explorer/interpreter/builtins.h b/explorer/interpreter/builtins.h index 0e77665b456b..db38fa70e4b4 100644 --- a/explorer/interpreter/builtins.h +++ b/explorer/interpreter/builtins.h @@ -40,6 +40,7 @@ class Builtins { AddWith, SubWith, MulWith, + DivWith, ModWith, // Bitwise and shift. @@ -64,6 +65,7 @@ class Builtins { static constexpr Builtin AddWith = Builtin::AddWith; static constexpr Builtin SubWith = Builtin::SubWith; static constexpr Builtin MulWith = Builtin::MulWith; + static constexpr Builtin DivWith = Builtin::DivWith; static constexpr Builtin ModWith = Builtin::ModWith; static constexpr Builtin BitComplement = Builtin::BitComplement; static constexpr Builtin BitAndWith = Builtin::BitAndWith; @@ -88,11 +90,11 @@ class Builtins { private: static constexpr int NumBuiltins = static_cast(Builtin::Last) + 1; static constexpr const char* BuiltinNames[NumBuiltins] = { - "As", "ImplicitAs", "EqWith", "LessWith", - "LessEqWith", "GreaterWith", "GreaterEqWith", "CompareWith", - "Negate", "AddWith", "SubWith", "MulWith", - "ModWith", "BitComplement", "BitAndWith", "BitOrWith", - "BitXorWith", "LeftShiftWith", "RightShiftWith"}; + "As", "ImplicitAs", "EqWith", "LessWith", + "LessEqWith", "GreaterWith", "GreaterEqWith", "CompareWith", + "Negate", "AddWith", "SubWith", "MulWith", + "DivWith", "ModWith", "BitComplement", "BitAndWith", + "BitOrWith", "BitXorWith", "LeftShiftWith", "RightShiftWith"}; std::optional> builtins_[NumBuiltins] = {}; }; diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 462476fe368f..a919bb948673 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -192,6 +192,9 @@ auto Interpreter::EvalPrim(Operator op, Nonnull static_type, case Operator::Mul: return arena_->New(cast(*args[0]).value() * cast(*args[1]).value()); + case Operator::Div: + return arena_->New(cast(*args[0]).value() / + cast(*args[1]).value()); case Operator::Mod: return arena_->New(cast(*args[0]).value() % cast(*args[1]).value()); diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 0260096e2a68..58eb693cf705 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -2061,6 +2061,8 @@ auto TypeChecker::TypeCheckExp(Nonnull e, return handle_binary_arithmetic(Builtins::SubWith); case Operator::Mul: return handle_binary_arithmetic(Builtins::MulWith); + case Operator::Div: + return handle_binary_arithmetic(Builtins::DivWith); case Operator::Mod: return handle_binary_arithmetic(Builtins::ModWith); case Operator::BitwiseAnd: diff --git a/explorer/syntax/parser.ypp b/explorer/syntax/parser.ypp index ea77ff5dbf63..8417ed37e50b 100644 --- a/explorer/syntax/parser.ypp +++ b/explorer/syntax/parser.ypp @@ -487,6 +487,12 @@ multiplicative_expression: context.source_loc(), Operator::Mul, std::vector>({$1, $3})); } +| multiplicative_lhs SLASH simple_binary_operand + { + $$ = arena->New( + context.source_loc(), Operator::Div, + std::vector>({$1, $3})); + } ; additive_operand: simple_binary_operand diff --git a/explorer/testdata/basic_syntax/fail_missing_var.carbon b/explorer/testdata/basic_syntax/fail_missing_var.carbon index 7b209be68ba7..45d1548b40ca 100644 --- a/explorer/testdata/basic_syntax/fail_missing_var.carbon +++ b/explorer/testdata/basic_syntax/fail_missing_var.carbon @@ -12,7 +12,7 @@ package ExplorerTest api; fn Main() -> i32 { // error - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL or SEMICOLON + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting SLASH or binary * x : i32; return 1; } diff --git a/explorer/testdata/basic_syntax/fail_var_named_self.carbon b/explorer/testdata/basic_syntax/fail_var_named_self.carbon index 79e9e2ded9e7..ba2c9cc3f2dc 100644 --- a/explorer/testdata/basic_syntax/fail_var_named_self.carbon +++ b/explorer/testdata/basic_syntax/fail_var_named_self.carbon @@ -14,7 +14,7 @@ fn Main() -> i32 { // Error: can't use keyword `Self` as the name of a variable. // TODO: Current error message is unclear, better would be to say // something like: unexpected `Self`, expecting identifier - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL or SEMICOLON + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting SLASH or binary * var Self : i32 = 0; return Self; } diff --git a/explorer/testdata/let/fail_local_named_self.carbon b/explorer/testdata/let/fail_local_named_self.carbon index 495f82098741..f60aece17df5 100644 --- a/explorer/testdata/let/fail_local_named_self.carbon +++ b/explorer/testdata/let/fail_local_named_self.carbon @@ -12,7 +12,7 @@ package ExplorerTest api; fn Main() -> i32 { // Error: Can't use keyword `Self` as the name of a local. - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting SLASH or binary * let Self: auto = 10; return 0; } diff --git a/explorer/testdata/operators/div.carbon b/explorer/testdata/operators/div.carbon new file mode 100644 index 000000000000..c15176a94330 --- /dev/null +++ b/explorer/testdata/operators/div.carbon @@ -0,0 +1,24 @@ +// 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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 2 + +package ExplorerTest api; + +class A { var n: i32; } + +external impl A as DivWith(i32) where .Result == A { + fn Op[me: Self](rhs: i32) -> A { return {.n = me.n / rhs}; } +} + +fn Main() -> i32 { + var a: A = {.n = 8}; + a = a / 3; + return a.n; +} diff --git a/explorer/testdata/operators/div_builtin.carbon b/explorer/testdata/operators/div_builtin.carbon new file mode 100644 index 000000000000..e73a4e3fc8e2 --- /dev/null +++ b/explorer/testdata/operators/div_builtin.carbon @@ -0,0 +1,24 @@ +// 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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: Interface: 2 +// CHECK: Op: 2 +// CHECK: result: 0 + +package ExplorerTest api; + +fn Main() -> i32 { + var lhs: i32 = 8; + var rhs: i32 = 3; + // Make sure that both the interface and operator work with i32. These rely on + // builtin arithmetic more directly. + Print("Interface: {0}", lhs.(DivWith(i32).Op)(rhs)); + Print("Op: {0}", lhs / rhs); + return 0; +}