From 8c287b4d842486895eadf8e3ec3766f27ca96d39 Mon Sep 17 00:00:00 2001 From: Shaikh Ubaid Date: Tue, 16 May 2023 15:39:15 +0530 Subject: [PATCH 1/4] Refactor: make parse_args() a function --- src/lpython/semantics/python_ast_to_asr.cpp | 31 +++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 49a6299395..38106d8e11 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -6599,6 +6599,14 @@ class BodyVisitor : public CommonVisitor { tmp = ASR::make_StringConstant_t(al, loc, s2c(al, s_var), str_type); } + void parse_args(const AST::Call_t &x, Vec &args) { + // Keyword arguments handled in make_call_helper() + if( x.n_keywords == 0 ) { + args.reserve(al, x.n_args); + visit_expr_list(x.m_args, x.n_args, args); + } + } + void visit_Call(const AST::Call_t &x) { std::string call_name = ""; Vec args; @@ -6612,14 +6620,9 @@ class BodyVisitor : public CommonVisitor { tmp = nullptr; return ; } - // Keyword arguments handled in make_call_helper - #define parse_args() if( x.n_keywords == 0 ) { \ - args.reserve(al, x.n_args); \ - visit_expr_list(x.m_args, x.n_args, args); \ - } \ if (AST::is_a(*x.m_func)) { - parse_args() + parse_args(x, args); AST::Attribute_t *at = AST::down_cast(x.m_func); if (AST::is_a(*at->m_value)) { AST::Name_t *n = AST::down_cast(at->m_value); @@ -6788,7 +6791,7 @@ class BodyVisitor : public CommonVisitor { // This will all be removed once we port it to intrinsic functions // Intrinsic functions if (call_name == "size") { - parse_args(); + parse_args(x, args);; if( args.size() < 1 || args.size() > 2 ) { throw SemanticError("array accepts only 1 (arr) or 2 (arr, axis) arguments, got " + std::to_string(args.size()) + " arguments instead.", @@ -6820,7 +6823,7 @@ class BodyVisitor : public CommonVisitor { tmp = nullptr; return; } else if (call_name == "callable") { - parse_args() + parse_args(x, args); if (args.size() != 1) { throw SemanticError(call_name + "() takes exactly one argument (" + std::to_string(args.size()) + " given)", x.base.base.loc); @@ -6836,13 +6839,13 @@ class BodyVisitor : public CommonVisitor { tmp = ASR::make_LogicalConstant_t(al, x.base.base.loc, result, type); return; } else if( call_name == "pointer" ) { - parse_args() + parse_args(x, args); ASR::ttype_t *type = ASRUtils::TYPE(ASR::make_Pointer_t(al, x.base.base.loc, ASRUtils::expr_type(args[0].m_value))); tmp = ASR::make_GetPointer_t(al, x.base.base.loc, args[0].m_value, type, nullptr); return ; } else if( call_name == "array" ) { - parse_args() + parse_args(x, args); if( args.size() != 1 ) { throw SemanticError("array accepts only 1 argument for now, got " + std::to_string(args.size()) + " arguments instead.", @@ -6862,7 +6865,7 @@ class BodyVisitor : public CommonVisitor { } return; } else if( call_name == "deepcopy" ) { - parse_args() + parse_args(x, args); if( args.size() != 1 ) { throw SemanticError("deepcopy only accepts one argument, found " + std::to_string(args.size()) + " instead.", @@ -6921,7 +6924,7 @@ class BodyVisitor : public CommonVisitor { call_name == "c32" || call_name == "c64" ) { - parse_args() + parse_args(x, args); ASR::ttype_t* target_type = nullptr; if( call_name == "i8" ) { target_type = ASRUtils::TYPE(ASR::make_Integer_t(al, x.base.base.loc, 1, nullptr, 0)); @@ -6953,7 +6956,7 @@ class BodyVisitor : public CommonVisitor { tmp = (ASR::asr_t*) arg; return ; } else if (intrinsic_node_handler.is_present(call_name)) { - parse_args() + parse_args(x, args); tmp = intrinsic_node_handler.get_intrinsic_node(call_name, al, x.base.base.loc, args); return; @@ -6965,7 +6968,7 @@ class BodyVisitor : public CommonVisitor { } // end of "comment" } - parse_args() + parse_args(x, args); tmp = make_call_helper(al, s, current_scope, args, call_name, x.base.base.loc, false, x.m_args, x.n_args, x.m_keywords, x.n_keywords); } From bc7c483a958be10b06341187e92330f903744f4e Mon Sep 17 00:00:00 2001 From: Shaikh Ubaid Date: Tue, 16 May 2023 17:01:23 +0530 Subject: [PATCH 2/4] ASR: Support named arguments in struct initialization --- src/lpython/semantics/python_ast_to_asr.cpp | 69 ++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 38106d8e11..18170cff8f 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -893,6 +893,63 @@ class CommonVisitor : public AST::BaseVisitor { return true; } + int64_t find_argument_position_from_name(ASR::StructType_t* orig_struct, std::string arg_name) { + int64_t arg_position = -1; + for( size_t i = 0; i < orig_struct->n_members; i++ ) { + std::string original_arg_name = std::string(orig_struct->m_members[i]); + if( original_arg_name == arg_name ) { + return i; + } + } + return arg_position; + } + + void visit_expr_list(AST::expr_t** pos_args, size_t n_pos_args, + AST::keyword_t* kwargs, size_t n_kwargs, + Vec& call_args_vec, + ASR::StructType_t* orig_struct, const Location &loc) { + LCOMPILERS_ASSERT(call_args_vec.reserve_called); + + // Fill the whole call_args_vec with nullptr + // This is for error handling later on. + for( size_t i = 0; i < n_pos_args + n_kwargs; i++ ) { + ASR::call_arg_t call_arg; + Location loc; + loc.first = loc.last = 1; + call_arg.m_value = nullptr; + call_arg.loc = loc; + call_args_vec.push_back(al, call_arg); + } + + // Now handle positional arguments in the following loop + for( size_t i = 0; i < n_pos_args; i++ ) { + this->visit_expr(*pos_args[i]); + ASR::expr_t* expr = ASRUtils::EXPR(tmp); + call_args_vec.p[i].loc = expr->base.loc; + call_args_vec.p[i].m_value = expr; + } + + // Now handle keyword arguments in the following loop + for( size_t i = 0; i < n_kwargs; i++ ) { + this->visit_expr(*kwargs[i].m_value); + ASR::expr_t* expr = ASRUtils::EXPR(tmp); + std::string arg_name = std::string(kwargs[i].m_arg); + int64_t arg_pos = find_argument_position_from_name(orig_struct, arg_name); + if( arg_pos == -1 ) { + throw SemanticError("Member '" + arg_name + "' not found in struct", kwargs[i].loc); + } else if (arg_pos >= (int64_t)call_args_vec.size()) { + throw SemanticError("Not enough arguments to " + std::string(orig_struct->m_name) + + "(), expected " + std::to_string(orig_struct->n_members), loc); + } + if( call_args_vec[arg_pos].m_value != nullptr ) { + throw SemanticError(std::string(orig_struct->m_name) + "() got multiple values for argument '" + + arg_name + "'", kwargs[i].loc); + } + call_args_vec.p[arg_pos].loc = expr->base.loc; + call_args_vec.p[arg_pos].m_value = expr; + } + } + void visit_expr_list_with_cast(ASR::expr_t** m_args, size_t n_args, Vec& call_args_vec, Vec& args, @@ -1195,7 +1252,17 @@ class CommonVisitor : public AST::BaseVisitor { } } else if(ASR::is_a(*s)) { ASR::StructType_t* StructType = ASR::down_cast(s); - for( size_t i = 0; i < std::min(args.size(), StructType->n_members); i++ ) { + if (n_kwargs > 0) { + args.reserve(al, n_pos_args + n_kwargs); + visit_expr_list(pos_args, n_pos_args, kwargs, n_kwargs, + args, StructType, loc); + } + + if (args.size() > 0 && args.size() != StructType->n_members) { + throw SemanticError("StructConstructor arguments do not match the number of struct members", loc); + } + + for( size_t i = 0; i < args.size(); i++ ) { std::string member_name = StructType->m_members[i]; ASR::Variable_t* member_var = ASR::down_cast( StructType->m_symtab->resolve_symbol(member_name)); From fe4584918e6288a94801314cb458dbf013dd312a Mon Sep 17 00:00:00 2001 From: Shaikh Ubaid Date: Tue, 16 May 2023 17:20:24 +0530 Subject: [PATCH 3/4] TEST: Add integration_tests for named args --- integration_tests/CMakeLists.txt | 2 ++ integration_tests/structs_23.py | 30 ++++++++++++++++++++++++++++++ integration_tests/structs_24.py | 18 ++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 integration_tests/structs_23.py create mode 100644 integration_tests/structs_24.py diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 67ee3b0621..e709d60ef9 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -478,6 +478,8 @@ RUN(NAME structs_20 LABELS cpython llvm c EXTRAFILES structs_20b.c) RUN(NAME structs_21 LABELS cpython llvm c) RUN(NAME structs_22 LABELS cpython llvm c) +RUN(NAME structs_23 LABELS cpython llvm c) +RUN(NAME structs_24 LABELS cpython llvm c) RUN(NAME sizeof_01 LABELS llvm c EXTRAFILES sizeof_01b.c) RUN(NAME sizeof_02 LABELS cpython llvm c) diff --git a/integration_tests/structs_23.py b/integration_tests/structs_23.py new file mode 100644 index 0000000000..2e3950a48b --- /dev/null +++ b/integration_tests/structs_23.py @@ -0,0 +1,30 @@ +from lpython import dataclass, i32, u64, f64 + +@dataclass +class A: + a: i32 + b: i32 + +@dataclass +class B: + a: u64 + b: f64 + +def main0(): + s: A = A(b=-24, a=6) + print(s.a) + print(s.b) + + assert s.a == 6 + assert s.b == -24 + +def main1(): + s: B = B(u64(22), b=3.14) + print(s.a) + print(s.b) + + assert s.a == u64(22) + assert abs(s.b - 3.14) <= 1e-12 + +main0() +main1() diff --git a/integration_tests/structs_24.py b/integration_tests/structs_24.py new file mode 100644 index 0000000000..10bf76c890 --- /dev/null +++ b/integration_tests/structs_24.py @@ -0,0 +1,18 @@ +from lpython import dataclass, i32, f64, u64 +from numpy import array + +@dataclass +class Foo: + x: i32 + y: i32 + +def main0() -> None: + foos: Foo[2] = array([Foo(y=2, x=1), Foo(x=3, y=4)]) + print(foos[0].x, foos[0].y, foos[1].x, foos[1].y) + + assert foos[0].x == 1 + assert foos[0].y == 2 + assert foos[1].x == 3 + assert foos[1].y == 4 + +main0() From 462a73123c24c9b5030a48cc2dd3aa054378535f Mon Sep 17 00:00:00 2001 From: Shaikh Ubaid Date: Tue, 16 May 2023 17:23:36 +0530 Subject: [PATCH 4/4] TEST: Add error tests named struct args --- tests/errors/structs_03.py | 10 ++++++++++ tests/errors/structs_04.py | 11 ++++++++++ tests/errors/structs_05.py | 11 ++++++++++ tests/errors/structs_06.py | 11 ++++++++++ tests/errors/structs_07.py | 11 ++++++++++ tests/reference/asr-structs_03-754fb64.json | 13 ++++++++++++ tests/reference/asr-structs_03-754fb64.stderr | 5 +++++ tests/reference/asr-structs_04-7b864bc.json | 13 ++++++++++++ tests/reference/asr-structs_04-7b864bc.stderr | 5 +++++ tests/reference/asr-structs_05-a89315d.json | 13 ++++++++++++ tests/reference/asr-structs_05-a89315d.stderr | 5 +++++ tests/reference/asr-structs_06-6e14537.json | 13 ++++++++++++ tests/reference/asr-structs_06-6e14537.stderr | 5 +++++ tests/reference/asr-structs_07-83694b7.json | 13 ++++++++++++ tests/reference/asr-structs_07-83694b7.stderr | 5 +++++ tests/tests.toml | 20 +++++++++++++++++++ 16 files changed, 164 insertions(+) create mode 100644 tests/errors/structs_03.py create mode 100644 tests/errors/structs_04.py create mode 100644 tests/errors/structs_05.py create mode 100644 tests/errors/structs_06.py create mode 100644 tests/errors/structs_07.py create mode 100644 tests/reference/asr-structs_03-754fb64.json create mode 100644 tests/reference/asr-structs_03-754fb64.stderr create mode 100644 tests/reference/asr-structs_04-7b864bc.json create mode 100644 tests/reference/asr-structs_04-7b864bc.stderr create mode 100644 tests/reference/asr-structs_05-a89315d.json create mode 100644 tests/reference/asr-structs_05-a89315d.stderr create mode 100644 tests/reference/asr-structs_06-6e14537.json create mode 100644 tests/reference/asr-structs_06-6e14537.stderr create mode 100644 tests/reference/asr-structs_07-83694b7.json create mode 100644 tests/reference/asr-structs_07-83694b7.stderr diff --git a/tests/errors/structs_03.py b/tests/errors/structs_03.py new file mode 100644 index 0000000000..467478f859 --- /dev/null +++ b/tests/errors/structs_03.py @@ -0,0 +1,10 @@ +from lpython import i32, dataclass + +@dataclass +class S: + x: i32 + +def main0(): + s: S = S(y=2) + +main0() diff --git a/tests/errors/structs_04.py b/tests/errors/structs_04.py new file mode 100644 index 0000000000..b9ebf4e14d --- /dev/null +++ b/tests/errors/structs_04.py @@ -0,0 +1,11 @@ +from lpython import i32, dataclass + +@dataclass +class S: + x: i32 + y: i32 + +def main0(): + s: S = S(24, x=2) + +main0() diff --git a/tests/errors/structs_05.py b/tests/errors/structs_05.py new file mode 100644 index 0000000000..3ac27e704c --- /dev/null +++ b/tests/errors/structs_05.py @@ -0,0 +1,11 @@ +from lpython import i32, dataclass + +@dataclass +class S: + x: i32 + y: i32 + +def main0(): + s: S = S(2) + +main0() diff --git a/tests/errors/structs_06.py b/tests/errors/structs_06.py new file mode 100644 index 0000000000..6a92987bac --- /dev/null +++ b/tests/errors/structs_06.py @@ -0,0 +1,11 @@ +from lpython import i32, dataclass + +@dataclass +class S: + x: i32 + y: i32 + +def main0(): + s: S = S(2, 3, 4, 5) + +main0() diff --git a/tests/errors/structs_07.py b/tests/errors/structs_07.py new file mode 100644 index 0000000000..71e22ab95e --- /dev/null +++ b/tests/errors/structs_07.py @@ -0,0 +1,11 @@ +from lpython import i32, dataclass + +@dataclass +class S: + x: i32 + y: i32 + +def main0(): + s: S = S(y=2) + +main0() diff --git a/tests/reference/asr-structs_03-754fb64.json b/tests/reference/asr-structs_03-754fb64.json new file mode 100644 index 0000000000..7050595260 --- /dev/null +++ b/tests/reference/asr-structs_03-754fb64.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-structs_03-754fb64", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/structs_03.py", + "infile_hash": "19180d0a7a22141e74e61452cc6cc185f1dd1c4f4315446450ce98db", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-structs_03-754fb64.stderr", + "stderr_hash": "c6410f9948863d922cb0a0cd36613c529ad45fdf556d393d36e2df07", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-structs_03-754fb64.stderr b/tests/reference/asr-structs_03-754fb64.stderr new file mode 100644 index 0000000000..2a1c1c0d91 --- /dev/null +++ b/tests/reference/asr-structs_03-754fb64.stderr @@ -0,0 +1,5 @@ +semantic error: Member 'y' not found in struct + --> tests/errors/structs_03.py:8:14 + | +8 | s: S = S(y=2) + | ^^^ diff --git a/tests/reference/asr-structs_04-7b864bc.json b/tests/reference/asr-structs_04-7b864bc.json new file mode 100644 index 0000000000..c188040d2f --- /dev/null +++ b/tests/reference/asr-structs_04-7b864bc.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-structs_04-7b864bc", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/structs_04.py", + "infile_hash": "5951c49d2d7f143bbe3d67b982770ceb6d709939eb2d5ed544888f16", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-structs_04-7b864bc.stderr", + "stderr_hash": "e4e04a1a30ae38b6587c4c3ad12a7e83839c63938c025a3884f62ef8", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-structs_04-7b864bc.stderr b/tests/reference/asr-structs_04-7b864bc.stderr new file mode 100644 index 0000000000..906b24c606 --- /dev/null +++ b/tests/reference/asr-structs_04-7b864bc.stderr @@ -0,0 +1,5 @@ +semantic error: S() got multiple values for argument 'x' + --> tests/errors/structs_04.py:9:18 + | +9 | s: S = S(24, x=2) + | ^^^ diff --git a/tests/reference/asr-structs_05-a89315d.json b/tests/reference/asr-structs_05-a89315d.json new file mode 100644 index 0000000000..b4e72a08e2 --- /dev/null +++ b/tests/reference/asr-structs_05-a89315d.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-structs_05-a89315d", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/structs_05.py", + "infile_hash": "3b94e692a074b226736f068daf39c876f113277a73468bd21c01d3cc", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-structs_05-a89315d.stderr", + "stderr_hash": "227decb39171becb34a42cbdd93d96bcdd4d8c9dc5151706a74d7074", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-structs_05-a89315d.stderr b/tests/reference/asr-structs_05-a89315d.stderr new file mode 100644 index 0000000000..66ac89a7b9 --- /dev/null +++ b/tests/reference/asr-structs_05-a89315d.stderr @@ -0,0 +1,5 @@ +semantic error: StructConstructor arguments do not match the number of struct members + --> tests/errors/structs_05.py:9:12 + | +9 | s: S = S(2) + | ^^^^ diff --git a/tests/reference/asr-structs_06-6e14537.json b/tests/reference/asr-structs_06-6e14537.json new file mode 100644 index 0000000000..e7e4818247 --- /dev/null +++ b/tests/reference/asr-structs_06-6e14537.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-structs_06-6e14537", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/structs_06.py", + "infile_hash": "9f4273c5fb4469837f65003255dcdca067c5c17735d0642757fd069c", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-structs_06-6e14537.stderr", + "stderr_hash": "21e94af3d6a631d4871d9bc2a86200c3c3c3b661964a079105721dde", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-structs_06-6e14537.stderr b/tests/reference/asr-structs_06-6e14537.stderr new file mode 100644 index 0000000000..fc29f775bd --- /dev/null +++ b/tests/reference/asr-structs_06-6e14537.stderr @@ -0,0 +1,5 @@ +semantic error: StructConstructor arguments do not match the number of struct members + --> tests/errors/structs_06.py:9:12 + | +9 | s: S = S(2, 3, 4, 5) + | ^^^^^^^^^^^^^ diff --git a/tests/reference/asr-structs_07-83694b7.json b/tests/reference/asr-structs_07-83694b7.json new file mode 100644 index 0000000000..4c07fec19b --- /dev/null +++ b/tests/reference/asr-structs_07-83694b7.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-structs_07-83694b7", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/structs_07.py", + "infile_hash": "4e4f15d47dc5d9cdadd653567f6d105365b7fe20b194663c10b3802a", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-structs_07-83694b7.stderr", + "stderr_hash": "6b1e1646f11ee384e5dce1ded0d91751f7113eaea7c90da5c1bcf4ff", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-structs_07-83694b7.stderr b/tests/reference/asr-structs_07-83694b7.stderr new file mode 100644 index 0000000000..13f0aaf0e1 --- /dev/null +++ b/tests/reference/asr-structs_07-83694b7.stderr @@ -0,0 +1,5 @@ +semantic error: Not enough arguments to S(), expected 2 + --> tests/errors/structs_07.py:9:12 + | +9 | s: S = S(y=2) + | ^^^^^^ diff --git a/tests/tests.toml b/tests/tests.toml index 21790026c9..cfb980ae44 100644 --- a/tests/tests.toml +++ b/tests/tests.toml @@ -642,6 +642,26 @@ asr = true filename = "errors/structs_02.py" asr = true +[[test]] +filename = "errors/structs_03.py" +asr = true + +[[test]] +filename = "errors/structs_04.py" +asr = true + +[[test]] +filename = "errors/structs_05.py" +asr = true + +[[test]] +filename = "errors/structs_06.py" +asr = true + +[[test]] +filename = "errors/structs_07.py" +asr = true + [[test]] filename = "errors/const_01.py" asr = true