From 42f385f6820b630cb9a40d44725e7fecc819a9fb Mon Sep 17 00:00:00 2001 From: Vipul Cariappa Date: Sat, 27 Jul 2024 09:40:38 +0530 Subject: [PATCH] Support to print dataclasses and tuples in REPL (#2785) * support to print dataclasses and tuples in REPL * commenting out the failing test --- src/libasr/codegen/evaluator.cpp | 5 + src/libasr/codegen/evaluator.h | 2 + src/libasr/pass/global_stmts.cpp | 10 +- src/lpython/python_evaluator.cpp | 65 +++++++-- src/lpython/semantics/python_ast_to_asr.cpp | 10 ++ src/lpython/tests/test_llvm.cpp | 154 ++++++++++++++++++++ 6 files changed, 234 insertions(+), 12 deletions(-) diff --git a/src/libasr/codegen/evaluator.cpp b/src/libasr/codegen/evaluator.cpp index cac4a4465b..1dff3131ff 100644 --- a/src/libasr/codegen/evaluator.cpp +++ b/src/libasr/codegen/evaluator.cpp @@ -95,6 +95,11 @@ llvm::Function *LLVMModule::get_function(const std::string &fn_name) { return m->getFunction(fn_name); } +llvm::GlobalVariable *LLVMModule::get_global(const std::string &global_name) { + llvm::Module *m = m_m.get(); + return m->getNamedGlobal(global_name); +} + std::string LLVMModule::get_return_type(const std::string &fn_name) { llvm::Module *m = m_m.get(); diff --git a/src/libasr/codegen/evaluator.h b/src/libasr/codegen/evaluator.h index 04de7481bc..fa2bf0e5c0 100644 --- a/src/libasr/codegen/evaluator.h +++ b/src/libasr/codegen/evaluator.h @@ -18,6 +18,7 @@ namespace llvm { class LLVMContext; class Module; class Function; + class GlobalVariable; class TargetMachine; class DataLayout; namespace orc { @@ -37,6 +38,7 @@ class LLVMModule // Return a function return type as a string (real / integer) std::string get_return_type(const std::string &fn_name); llvm::Function *get_function(const std::string &fn_name); + llvm::GlobalVariable *get_global(const std::string &global_name); }; class LLVMEvaluator diff --git a/src/libasr/pass/global_stmts.cpp b/src/libasr/pass/global_stmts.cpp index 4295d37ca8..7fc1e8e6c4 100644 --- a/src/libasr/pass/global_stmts.cpp +++ b/src/libasr/pass/global_stmts.cpp @@ -52,7 +52,8 @@ void pass_wrap_global_stmts(Allocator &al, (ASRUtils::expr_type(value)->type == ASR::ttypeType::Complex) || (ASRUtils::expr_type(value)->type == ASR::ttypeType::Character) || (ASRUtils::expr_type(value)->type == ASR::ttypeType::List) || - (ASRUtils::expr_type(value)->type == ASR::ttypeType::Tuple)) { + (ASRUtils::expr_type(value)->type == ASR::ttypeType::Tuple) || + (ASRUtils::expr_type(value)->type == ASR::ttypeType::StructType)) { s.from_str(al, fn_name_s + std::to_string(idx)); var_name = s.c_str(al); type = ASRUtils::expr_type(value); @@ -102,6 +103,13 @@ void pass_wrap_global_stmts(Allocator &al, unit.m_symtab->add_symbol(global_underscore_name, down_cast(global_underscore)); ASR::stmt_t* asr_stmt = ASRUtils::STMT(ASR::make_Assignment_t(al, loc, global_underscore_ref, return_var_ref, nullptr)); body.push_back(al, asr_stmt); + + if ((ASRUtils::expr_type(return_var_ref)->type == ASR::ttypeType::List) || + (ASRUtils::expr_type(return_var_ref)->type == ASR::ttypeType::Tuple) || + (ASRUtils::expr_type(return_var_ref)->type == ASR::ttypeType::StructType)) { + return_var_ref = nullptr; + return_var = nullptr; + } } ASR::asr_t *fn = ASRUtils::make_Function_t_util( diff --git a/src/lpython/python_evaluator.cpp b/src/lpython/python_evaluator.cpp index f11b98d5df..f4fa810f39 100644 --- a/src/lpython/python_evaluator.cpp +++ b/src/lpython/python_evaluator.cpp @@ -16,6 +16,7 @@ #ifdef HAVE_LFORTRAN_LLVM #include #include +#include #include #include #include @@ -123,7 +124,8 @@ Result PythonCompiler::evaluate( result.llvm_ir = m->str(); } - ASR::symbol_t *global_underscore_symbol = symbol_table->get_symbol("_" + run_fn); + ASR::symbol_t *global_underscore_symbol = ASR::down_cast(symbol_table->resolve_symbol(module_name)) + ->m_symtab->get_symbol("_" + run_fn); if (global_underscore_symbol) { global_underscore_name = "_" + run_fn; } @@ -134,8 +136,10 @@ Result PythonCompiler::evaluate( call_run_fn = true; } - ASR::symbol_t *global_underscore_sym = symbol_table->get_symbol(global_underscore_name); - if ((return_type == "struct") && (global_underscore_sym)) { + bool struct_to_print = false; + ASR::symbol_t *global_underscore_sym = ASR::down_cast(symbol_table->resolve_symbol(module_name)) + ->m_symtab->get_symbol("_" + run_fn); + if ((return_type == "void") && (global_underscore_sym)) { // we compute the offsets of the struct's attribute here // we will be using it later in aggregate_type_to_string to print the struct @@ -143,10 +147,13 @@ Result PythonCompiler::evaluate( // because once we call `e->add_module`, internally LLVM may deallocate all the // type info after compiling the IR into machine code - llvm::Function *fn = m->get_function(run_fn); - llvm::Type *llvm_type = fn->getReturnType(); - LCOMPILERS_ASSERT(llvm_type->isStructTy()) - compute_offsets(llvm_type, global_underscore_sym, result); + llvm::GlobalVariable *g = m->get_global("_" + run_fn); + LCOMPILERS_ASSERT(g) + llvm::Type *llvm_type = g->getValueType(); + if (llvm_type->isStructTy()) { + struct_to_print = true; + compute_offsets(llvm_type, global_underscore_sym, result); + } } e->add_module(std::move(m)); @@ -246,7 +253,14 @@ Result PythonCompiler::evaluate( } } else if (return_type == "void") { e->execfn(run_fn); - result.type = EvalResult::statement; + if (global_underscore_sym && struct_to_print) { + void *r = (void*)e->get_symbol_address("_" + run_fn); + LCOMPILERS_ASSERT(r) + result.structure.structure = r; + result.type = EvalResult::struct_type; + } else { + result.type = EvalResult::statement; + } } else if (return_type == "none") { result.type = EvalResult::none; } else { @@ -259,8 +273,9 @@ Result PythonCompiler::evaluate( ->erase_symbol(run_fn); } if (global_underscore_symbol) { - if (symbol_table->resolve_symbol("_")) { - symbol_table->erase_symbol("_"); + if (ASR::down_cast(symbol_table->resolve_symbol(module_name))->m_symtab->resolve_symbol("_")) { + ASR::down_cast(symbol_table->resolve_symbol(module_name))->m_symtab + ->erase_symbol("_"); } ASR::Variable_t *a = ASR::down_cast(global_underscore_symbol); ASR::Variable_t *b = al.make_new(); @@ -268,7 +283,8 @@ Result PythonCompiler::evaluate( Str s; s.from_str(al, "_"); b->m_name = s.c_str(al); - symbol_table->add_symbol("_", ASR::down_cast((ASR::asr_t*)b)); + ASR::down_cast(symbol_table->resolve_symbol(module_name))->m_symtab + ->add_symbol("_", ASR::down_cast((ASR::asr_t*)b)); } eval_count++; @@ -515,6 +531,33 @@ std::string PythonCompiler::aggregate_type_to_string(const struct EvalResult &r) print_type(element_ttype, ((char*)array)+((size - 1)*element_size), result); result += "]"; + } else if (asr_type->type == ASR::ttypeType::Tuple) { + ASR::Tuple_t *tuple_type = ASR::down_cast(asr_type); + result += "("; + for (size_t i = 0; i < tuple_type->n_type - 1; i++) { + print_type(tuple_type->m_type[i], ((char*)data)+offsets[i], result); + result += ", "; + } + print_type(tuple_type->m_type[tuple_type->n_type - 1], ((char*)data)+offsets[tuple_type->n_type - 1], result); + result += ")"; + + } else if (asr_type->type == ASR::ttypeType::StructType) { + ASR::StructType_t *class_type = ASR::down_cast(asr_type); + ASR::Struct_t *struct_info = ASR::down_cast(class_type->m_derived_type); + LCOMPILERS_ASSERT(class_type->n_data_member_types == struct_info->n_members) + result += struct_info->m_name; + result += "("; + for (size_t i = 0; i < struct_info->n_members - 1; i++) { + result += struct_info->m_members[i]; + result += "="; + print_type(class_type->m_data_member_types[i], ((char*)data)+offsets[i], result); + result += ", "; + } + result += struct_info->m_members[struct_info->n_members - 1]; + result += "="; + print_type(class_type->m_data_member_types[struct_info->n_members - 1], ((char*)data)+offsets[struct_info->n_members - 1], result); + result += ")"; + } else { throw LCompilersException("PythonCompiler::evaluate(): Return type not supported"); } diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 4715cf8857..4e32544167 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -5122,6 +5122,16 @@ class BodyVisitor : public CommonVisitor { // Erase the function in TranslationUnit unit->m_symtab->erase_symbol(func_name); } + ASR::symbol_t *g_sym = unit->m_symtab->get_symbol("_" + func_name); + if (g_sym) { + // Move the `global_underscore` variable into the + // Module from TranslationUnit + ASR::Variable_t *f = ASR::down_cast(g_sym); + f->m_parent_symtab = mod->m_symtab; + mod->m_symtab->add_symbol("_" + func_name, (ASR::symbol_t *) f); + // Erase the function in TranslationUnit + unit->m_symtab->erase_symbol("_" + func_name); + } items.p = nullptr; items.n = 0; } diff --git a/src/lpython/tests/test_llvm.cpp b/src/lpython/tests/test_llvm.cpp index 22a185bd1d..831f66584f 100644 --- a/src/lpython/tests/test_llvm.cpp +++ b/src/lpython/tests/test_llvm.cpp @@ -1506,6 +1506,160 @@ TEST_CASE("PythonCompiler lists") { CHECK(e.aggregate_type_to_string(r.result) == "[\"lfortran\", \"lpython\", \"lc\"]"); } +TEST_CASE("PythonCompiler tuples") { + CompilerOptions cu; + cu.po.disable_main = true; + cu.emit_debug_line_column = false; + cu.generate_object_code = false; + cu.interactive = true; + cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir(); + PythonCompiler e(cu); + LCompilers::Result + + r = e.evaluate2("(1, 2)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "tuple[i32, i32]"); + CHECK(e.aggregate_type_to_string(r.result) == "(1, 2)"); + + r = e.evaluate2("(1, 2, 2.5)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "tuple[i32, i32, r64]"); + CHECK(e.aggregate_type_to_string(r.result) == "(1, 2, 2.500000)"); + + r = e.evaluate2("(1, 2, 2.5, \"LPython\")"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "tuple[i32, i32, r64, str]"); + CHECK(e.aggregate_type_to_string(r.result) == "(1, 2, 2.500000, \"LPython\")"); + + r = e.evaluate2("(1, 2, 2.5, \"LPython\", True)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "tuple[i32, i32, r64, str, i1]"); + CHECK(e.aggregate_type_to_string(r.result) == "(1, 2, 2.500000, \"LPython\", True)"); + + r = e.evaluate2("(i8(1), i16(1), i64(1))"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "tuple[i8, i16, i64]"); + CHECK(e.aggregate_type_to_string(r.result) == "(1, 1, 1)"); + + r = e.evaluate2("(f32(1.0),)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "tuple[r32]"); + CHECK(e.aggregate_type_to_string(r.result) == "(1.000000)"); +} + +TEST_CASE("PythonCompiler classes") { + CompilerOptions cu; + cu.po.disable_main = true; + cu.emit_debug_line_column = false; + cu.generate_object_code = false; + cu.interactive = true; + cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir(); + PythonCompiler e(cu); + LCompilers::Result + + r = e.evaluate2(R"( +@dataclass +class MyClass1: + x: i32 +)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::none); + + r = e.evaluate2("c1: MyClass1 = MyClass1(12)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::statement); + + r = e.evaluate2("c1"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(e.aggregate_type_to_string(r.result) == "MyClass1(x=12)"); + + r = e.evaluate2(R"( +@dataclass +class MyClass2: + i: i32 + f: f64 +)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::none); + + r = e.evaluate2("c2: MyClass2 = MyClass2(12, 2.5)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::statement); + + r = e.evaluate2("c2"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(e.aggregate_type_to_string(r.result) == "MyClass2(i=12, f=2.500000)"); + + r = e.evaluate2(R"( +@dataclass +class MyClass3: + i: i32 + f: f64 + s: str +)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::none); + + r = e.evaluate2("c3: MyClass3 = MyClass3(12, 2.5, \"LPython\")"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::statement); + + r = e.evaluate2("c3"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(e.aggregate_type_to_string(r.result) == "MyClass3(i=12, f=2.500000, s=\"LPython\")"); + + r = e.evaluate2(R"( +@dataclass +class MyClass4: + i_1: bool + i_8: i8 + i_16: i16 + i_32: i32 + i_64: i64 +)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::none); + + r = e.evaluate2("c4: MyClass4 = MyClass4(True, i8(2), i16(3), i32(4), i64(5))"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::statement); + + r = e.evaluate2("c4"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + // CHECK(e.aggregate_type_to_string(r.result) == "MyClass4(i_1=True, i_8=2, i_16=3, i_32=4, i_64=5)"); // FIXME: look at issue #2793 + + r = e.evaluate2(R"( +@dataclass +class MyClass5: + u_1: bool + u_8: u8 + u_16: u16 + u_32: u32 + u_64: u64 +)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::none); + + r = e.evaluate2("c5: MyClass5 = MyClass5(False, u8(2), u16(3), u32(4), u64(5))"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::statement); + + r = e.evaluate2("c5"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(e.aggregate_type_to_string(r.result) == "MyClass5(u_1=False, u_8=2, u_16=3, u_32=4, u_64=5)"); +} + TEST_CASE("PythonCompiler underscore 1") { CompilerOptions cu; cu.po.disable_main = true;