Преглед изворни кода

Support basic int printing (#1405)

We don't have overload resolution, so I've simply added PrintInt for now. But it's really tempting to turn __intrinsic_print into a total kludge function.
Jon Ross-Perkins пре 3 година
родитељ
комит
a41915b5af

+ 5 - 0
explorer/data/prelude.carbon

@@ -40,6 +40,11 @@ fn Print(format_str: String) {
   __intrinsic_print(format_str);
 }
 
+// Neither overloads nor variadics are supported, so using a different name.
+fn PrintInt(format_str: String, arg0: i32) {
+  __intrinsic_print(format_str, arg0);
+}
+
 class Heap {
   fn New[T:! Type, me: Self](x : T) -> T* {
     return __intrinsic_new(x);

+ 17 - 3
explorer/interpreter/interpreter.cpp

@@ -22,6 +22,7 @@
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
 
 using llvm::cast;
 using llvm::dyn_cast;
@@ -1132,9 +1133,22 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
       // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}
       switch (cast<IntrinsicExpression>(exp).intrinsic()) {
         case IntrinsicExpression::Intrinsic::Print: {
-          const auto& args = cast<TupleValue>(*act.results()[0]);
-          // TODO: This could eventually use something like llvm::formatv.
-          llvm::outs() << cast<StringValue>(*args.elements()[0]).value();
+          const auto& args = cast<TupleValue>(*act.results()[0]).elements();
+          switch (args.size()) {
+            case 1:
+              llvm::outs() << llvm::formatv(
+                  cast<StringValue>(*args[0]).value().c_str());
+              break;
+            case 2:
+              llvm::outs() << llvm::formatv(
+                  cast<StringValue>(*args[0]).value().c_str(),
+                  cast<IntValue>(*args[1]).value());
+              break;
+            default:
+              CARBON_FATAL() << "Unexpected arg count: " << args.size();
+          }
+          // Implicit newline; currently no way to disable it.
+          llvm::outs() << "\n";
           return todo_.FinishAction(TupleValue::Empty());
         }
         case IntrinsicExpression::Intrinsic::Alloc: {

+ 15 - 9
explorer/interpreter/type_checker.cpp

@@ -2156,35 +2156,41 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
     case ExpressionKind::IntrinsicExpression: {
       auto& intrinsic_exp = cast<IntrinsicExpression>(*e);
       CARBON_RETURN_IF_ERROR(TypeCheckExp(&intrinsic_exp.args(), impl_scope));
+      const auto& args = intrinsic_exp.args().fields();
       switch (cast<IntrinsicExpression>(*e).intrinsic()) {
         case IntrinsicExpression::Intrinsic::Print:
-          if (intrinsic_exp.args().fields().size() != 1) {
+          if (args.size() < 1 || args.size() > 2) {
             return CompilationError(e->source_loc())
-                   << "__intrinsic_print takes 1 argument";
+                   << "__intrinsic_print takes 1 or 2 arguments, received "
+                   << args.size();
           }
           CARBON_RETURN_IF_ERROR(ExpectExactType(
-              e->source_loc(), "__intrinsic_print argument",
-              arena_->New<StringType>(),
-              &intrinsic_exp.args().fields()[0]->static_type(), impl_scope));
+              e->source_loc(), "__intrinsic_print argument 0",
+              arena_->New<StringType>(), &args[0]->static_type(), impl_scope));
+          if (args.size() >= 2) {
+            CARBON_RETURN_IF_ERROR(ExpectExactType(
+                e->source_loc(), "__intrinsic_print argument 1",
+                arena_->New<IntType>(), &args[1]->static_type(), impl_scope));
+          }
           e->set_static_type(TupleValue::Empty());
           e->set_value_category(ValueCategory::Let);
           return Success();
         case IntrinsicExpression::Intrinsic::Alloc: {
-          if (intrinsic_exp.args().fields().size() != 1) {
+          if (args.size() != 1) {
             return CompilationError(e->source_loc())
                    << "__intrinsic_new takes 1 argument";
           }
-          auto arg_type = &intrinsic_exp.args().fields()[0]->static_type();
+          auto arg_type = &args[0]->static_type();
           e->set_static_type(arena_->New<PointerType>(arg_type));
           e->set_value_category(ValueCategory::Let);
           return Success();
         }
         case IntrinsicExpression::Intrinsic::Dealloc: {
-          if (intrinsic_exp.args().fields().size() != 1) {
+          if (args.size() != 1) {
             return CompilationError(e->source_loc())
                    << "__intrinsic_new takes 1 argument";
           }
-          auto arg_type = &intrinsic_exp.args().fields()[0]->static_type();
+          auto arg_type = &args[0]->static_type();
           CARBON_RETURN_IF_ERROR(
               ExpectPointerType(e->source_loc(), "*", arg_type));
           e->set_static_type(TupleValue::Empty());

+ 9 - 3
explorer/testdata/alias/class_alias.carbon

@@ -7,7 +7,10 @@
 // RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
 // RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
 // AUTOUPDATE: %{explorer} %s
-// CHECK: result: 123
+// CHECK: b.n: 1
+// CHECK: d.Get(0): 2
+// CHECK: e.Get(1): 3
+// CHECK: result: 0
 
 package ExplorerTest api;
 
@@ -35,6 +38,9 @@ fn Main() -> i32 {
   var c: GenericClass(i32) = {.m = 2};
   var d: GenericClassAlias(i32) = c;
   var e: ClassSpecializationAlias = c;
-  // TODO: Switch to using Print here once it supports printing integers.
-  return 100 * b.n + 10 * d.Get(0) + e.Get(1);
+
+  PrintInt("b.n: {0}", b.n);
+  PrintInt("d.Get(0): {0}", d.Get(0));
+  PrintInt("e.Get(1): {0}", e.Get(1));
+  return 0;
 }

+ 10 - 3
explorer/testdata/alias/struct_alias.carbon

@@ -7,7 +7,11 @@
 // RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
 // RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
 // AUTOUPDATE: %{explorer} %s
-// CHECK: result: 2121
+// CHECK: ab.a: 2
+// CHECK: ab.b: 1
+// CHECK: ba.a: 2
+// CHECK: ba.b: 1
+// CHECK: result: 0
 
 package ExplorerTest api;
 
@@ -17,6 +21,9 @@ alias BA = {.b: i32, .a: i32};
 fn Main() -> i32 {
   var ab: AB = {.b = 1, .a = 2};
   var ba: BA = ab;
-  // TODO: Switch to using Print here once it supports printing integers.
-  return 1000 * ab.a + 100 * ab.b + 10 * ba.a + ba.b;
+  PrintInt("ab.a: {0}", ab.a);
+  PrintInt("ab.b: {0}", ab.b);
+  PrintInt("ba.a: {0}", ba.a);
+  PrintInt("ba.b: {0}", ba.b);
+  return 0;
 }

+ 1 - 1
explorer/testdata/basic_syntax/print.carbon

@@ -13,7 +13,7 @@
 package ExplorerTest api;
 
 fn Main() -> i32 {
-  var s: auto = "Hello world!\n";
+  var s: auto = "Hello world!";
   Print(s);
   return 0;
 }

+ 18 - 0
explorer/testdata/basic_syntax/print_i32.carbon

@@ -0,0 +1,18 @@
+// 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: Printing 1
+// CHECK: result: 0
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  PrintInt("Printing {0}", 1);
+  return 0;
+}

+ 5 - 5
explorer/testdata/member_access/evaluate_type_before_dot.carbon

@@ -35,7 +35,7 @@ fn GetConstraint() -> Type { return Interface & AnotherInterface; }
 fn TestStruct() {
   var s: GetStruct() = {.n = 1};
   if (s.(GetStruct().n) == 1) {
-    Print("Struct OK\n");
+    Print("Struct OK");
   }
 }
 
@@ -43,28 +43,28 @@ fn TestChoice() {
   var c: GetChoice() = GetChoice().Alternative();
   match (c) {
     case GetChoice().Alternative() => {
-      Print("Choice OK\n");
+      Print("Choice OK");
     }
   }
 }
 
 fn TestClass() {
   if (GetClass().F(1) == 2) {
-    Print("Class OK\n");
+    Print("Class OK");
   }
 }
 
 fn TestInterface() {
   var n: i32 = 1;
   if (n.(GetInterface().G)() == 2) {
-    Print("Interface OK\n");
+    Print("Interface OK");
   }
 }
 
 fn TestConstraint() {
   var n: i32 = 1;
   if (n.(GetConstraint().G)() == 2) {
-    Print("Constraint OK\n");
+    Print("Constraint OK");
   }
 }