Просмотр исходного кода

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 *.
Junhee Cho 3 лет назад
Родитель
Сommit
69d4363ea5

+ 1 - 0
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;

+ 4 - 0
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;

+ 2 - 0
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 "-";

+ 1 - 0
explorer/ast/expression.h

@@ -123,6 +123,7 @@ enum class Operator {
   BitShiftRight,
   Complement,
   Deref,
+  Div,
   Eq,
   Less,
   LessEq,

+ 10 - 0
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; }
 }

+ 2 - 0
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:

+ 7 - 5
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<int>(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<Nonnull<const Declaration*>> builtins_[NumBuiltins] = {};
 };

+ 3 - 0
explorer/interpreter/interpreter.cpp

@@ -192,6 +192,9 @@ auto Interpreter::EvalPrim(Operator op, Nonnull<const Value*> static_type,
     case Operator::Mul:
       return arena_->New<IntValue>(cast<IntValue>(*args[0]).value() *
                                    cast<IntValue>(*args[1]).value());
+    case Operator::Div:
+      return arena_->New<IntValue>(cast<IntValue>(*args[0]).value() /
+                                   cast<IntValue>(*args[1]).value());
     case Operator::Mod:
       return arena_->New<IntValue>(cast<IntValue>(*args[0]).value() %
                                    cast<IntValue>(*args[1]).value());

+ 2 - 0
explorer/interpreter/type_checker.cpp

@@ -2061,6 +2061,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> 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:

+ 6 - 0
explorer/syntax/parser.ypp

@@ -487,6 +487,12 @@ multiplicative_expression:
           context.source_loc(), Operator::Mul,
           std::vector<Nonnull<Expression*>>({$1, $3}));
     }
+| multiplicative_lhs SLASH simple_binary_operand
+    {
+      $$ = arena->New<OperatorExpression>(
+          context.source_loc(), Operator::Div,
+          std::vector<Nonnull<Expression*>>({$1, $3}));
+    }
 ;
 additive_operand:
   simple_binary_operand

+ 1 - 1
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;
 }

+ 1 - 1
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;
 }

+ 1 - 1
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;
 }

+ 24 - 0
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;
+}

+ 24 - 0
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;
+}