From a41915b5af192bf6666f55b70c993367941c5b13 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Sun, 17 Jul 2022 20:35:44 -0700 Subject: [PATCH] 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. --- explorer/data/prelude.carbon | 5 ++++ explorer/interpreter/interpreter.cpp | 20 +++++++++++++--- explorer/interpreter/type_checker.cpp | 24 ++++++++++++------- explorer/testdata/alias/class_alias.carbon | 12 +++++++--- explorer/testdata/alias/struct_alias.carbon | 13 +++++++--- explorer/testdata/basic_syntax/print.carbon | 2 +- .../testdata/basic_syntax/print_i32.carbon | 18 ++++++++++++++ .../evaluate_type_before_dot.carbon | 10 ++++---- 8 files changed, 80 insertions(+), 24 deletions(-) create mode 100644 explorer/testdata/basic_syntax/print_i32.carbon diff --git a/explorer/data/prelude.carbon b/explorer/data/prelude.carbon index 9c5112c379f6..e82dd6cc3063 100644 --- a/explorer/data/prelude.carbon +++ b/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); diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index d936d35b52da..e8604d940224 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/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 { // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} switch (cast(exp).intrinsic()) { case IntrinsicExpression::Intrinsic::Print: { - const auto& args = cast(*act.results()[0]); - // TODO: This could eventually use something like llvm::formatv. - llvm::outs() << cast(*args.elements()[0]).value(); + const auto& args = cast(*act.results()[0]).elements(); + switch (args.size()) { + case 1: + llvm::outs() << llvm::formatv( + cast(*args[0]).value().c_str()); + break; + case 2: + llvm::outs() << llvm::formatv( + cast(*args[0]).value().c_str(), + cast(*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: { diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 53120be5354c..b5ecf78e9b31 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -2156,35 +2156,41 @@ auto TypeChecker::TypeCheckExp(Nonnull e, case ExpressionKind::IntrinsicExpression: { auto& intrinsic_exp = cast(*e); CARBON_RETURN_IF_ERROR(TypeCheckExp(&intrinsic_exp.args(), impl_scope)); + const auto& args = intrinsic_exp.args().fields(); switch (cast(*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(), - &intrinsic_exp.args().fields()[0]->static_type(), impl_scope)); + e->source_loc(), "__intrinsic_print argument 0", + arena_->New(), &args[0]->static_type(), impl_scope)); + if (args.size() >= 2) { + CARBON_RETURN_IF_ERROR(ExpectExactType( + e->source_loc(), "__intrinsic_print argument 1", + arena_->New(), &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(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()); diff --git a/explorer/testdata/alias/class_alias.carbon b/explorer/testdata/alias/class_alias.carbon index 624b6921767a..62f5c166d842 100644 --- a/explorer/testdata/alias/class_alias.carbon +++ b/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; } diff --git a/explorer/testdata/alias/struct_alias.carbon b/explorer/testdata/alias/struct_alias.carbon index b36060a80334..e8771c6f5524 100644 --- a/explorer/testdata/alias/struct_alias.carbon +++ b/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; } diff --git a/explorer/testdata/basic_syntax/print.carbon b/explorer/testdata/basic_syntax/print.carbon index 8df624f6468e..e7a9cc347083 100644 --- a/explorer/testdata/basic_syntax/print.carbon +++ b/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; } diff --git a/explorer/testdata/basic_syntax/print_i32.carbon b/explorer/testdata/basic_syntax/print_i32.carbon new file mode 100644 index 000000000000..4632668000a9 --- /dev/null +++ b/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; +} diff --git a/explorer/testdata/member_access/evaluate_type_before_dot.carbon b/explorer/testdata/member_access/evaluate_type_before_dot.carbon index 09277dd28425..bc3795c3e385 100644 --- a/explorer/testdata/member_access/evaluate_type_before_dot.carbon +++ b/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"); } }