diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index d19918b843..517b40dd9a 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -790,6 +790,7 @@ RUN(NAME test_statistics_01 LABELS cpython llvm llvm_jit NOFAST) RUN(NAME test_statistics_02 LABELS cpython llvm llvm_jit NOFAST REQ_PY_VER 3.10) RUN(NAME test_str_attributes LABELS cpython llvm llvm_jit c) RUN(NAME kwargs_01 LABELS cpython llvm llvm_jit c NOFAST) +RUN(NAME def_func_01 LABELS cpython llvm llvm_jit c) RUN(NAME func_inline_01 LABELS llvm llvm_jit c wasm) RUN(NAME func_inline_02 LABELS cpython llvm llvm_jit c) diff --git a/integration_tests/def_func_01.py b/integration_tests/def_func_01.py new file mode 100644 index 0000000000..2564cff13e --- /dev/null +++ b/integration_tests/def_func_01.py @@ -0,0 +1,76 @@ +from lpython import i32,i64 + +def factorial_1(x: i32, y:i32 =1) ->i32 : + if x <= 1: + return y + return x * factorial_1(x-1) + +def factorial_2(x: i32, y:i32=3 ,z:i32 =2) ->i32: + if x ==4: + return x * y * z + return x * factorial_2(x-1) + +def default_func(x : str ="Hello", y : str = " ", z : str = "World") ->str: + return x + y + z + + +def even_positions(iterator : i32, to_add : str = "?")-> str: + if (iterator == 10): return "" + if iterator%2 == 0 : + return to_add + even_positions(iterator+1,"X") + return to_add +even_positions(iterator+1) + + + +def test_factorial_1(): + test_00 : i32 = factorial_1(1) + print("test_00 is =>", test_00) + assert test_00 == 1 + + test_01 : i32 = factorial_1(5,0) + print("test_01 is =>", test_01) + assert test_01 == 120 + + test_02 : i32 = factorial_1(1,5555) + print("test_02 is =>", test_02) + assert test_02 == 5555 + +def test_factorial_2(): + test_03 : i32 =factorial_2(5,99999,99999) + print("test_03 is =>", test_03) + assert test_03 == 120 + + test_04 : i32 = factorial_2(4,-1,100) + print("test_04 is =>", test_04) + assert test_04 == -400 + +def test_default_func(): + test_05 :str = default_func() + print("test_05 is =>", test_05) + assert test_05 == "Hello World" + + test_06 :str = default_func(y = "|||",x="Hi") + print("test_06 is =>", test_06) + assert test_06 == "Hi|||World" + + test_07 :str = default_func(y = "++",z = "LPython") + print("test_07 is =>", test_07) + assert test_07 == "Hello++LPython" + + test_8 :str = default_func("Welcome",z = "LPython") + print("test_8 is =>", test_8) + assert test_8 == "Welcome LPython" + +def test_even_positions(): + test_09 : str = even_positions(0) + print("test_09 is =>", test_09) + assert test_09 == "?X?X?X?X?X" + + test_10 : str = even_positions(0,"W") + print("test_10 is =>", test_10) + assert test_10 == "WX?X?X?X?X" + +test_factorial_1() +test_factorial_2() +test_default_func() +test_even_positions() diff --git a/src/libasr/codegen/asr_to_c_cpp.h b/src/libasr/codegen/asr_to_c_cpp.h index fbbaa35fc4..9b980767ad 100644 --- a/src/libasr/codegen/asr_to_c_cpp.h +++ b/src/libasr/codegen/asr_to_c_cpp.h @@ -565,6 +565,7 @@ R"(#include if( is_c ) { CDeclarationOptions c_decl_options; c_decl_options.pre_initialise_derived_type = false; + c_decl_options.do_not_initialize = true; func += self().convert_variable_decl(*arg, &c_decl_options); } else { CPPDeclarationOptions cpp_decl_options; diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 59640c6fd8..9a06a8453b 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -659,7 +659,7 @@ class CommonVisitor : public AST::BaseVisitor { // 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++ ) { + for( size_t i = 0; i < orig_func->n_args; i++ ) { ASR::call_arg_t call_arg; Location loc; loc.first = loc.last = 1; @@ -704,6 +704,31 @@ class CommonVisitor : public AST::BaseVisitor { call_args_vec.p[arg_pos].loc = expr->base.loc; call_args_vec.p[arg_pos].m_value = expr; } + // Filling missing arguments with their defaults passed in function definition (if present). + std::string missed_args_names; + size_t missed_args_count =0; + for(size_t i = 0; i < orig_func->n_args; i++ ){ + if(call_args_vec.p[i].m_value == nullptr){ + ASR::Variable_t* var = ASRUtils::EXPR2VAR(orig_func->m_args[i]); + if (var->m_symbolic_value == nullptr){ + missed_args_names+="'" + (std::string) var->m_name + "' and "; + missed_args_count++; + } else { + call_args_vec.p[i].m_value = var->m_symbolic_value; + } + } + } + if(missed_args_count > 0){ + missed_args_names = missed_args_names.substr(0,missed_args_names.length() - 5); + diag.add(diag::Diagnostic( + "Number of arguments does not match in the function call", + diag::Level::Error, diag::Stage::Semantic, { + diag::Label("missing " + std::to_string(missed_args_count) + " required arguments :" + missed_args_names, + {call_loc}) + }) + ); + throw SemanticAbort(); + } return true; } @@ -1139,9 +1164,35 @@ class CommonVisitor : public AST::BaseVisitor { if (ASR::is_a(*s)) { ASR::Function_t *func = ASR::down_cast(s); if( n_kwargs > 0 && !is_generic_procedure ) { - args.reserve(al, n_pos_args + n_kwargs); + args.reserve(al, func->n_args); visit_expr_list(pos_args, n_pos_args, kwargs, n_kwargs, args, rt_subs, func, loc); + } else if (args.size() < func->n_args) { + std::string missed_args_names =" "; + size_t missed_args_count =0; + for (size_t def_arg = args.size(); def_arg < func->n_args; def_arg++){ + ASR::Variable_t* var = ASRUtils::EXPR2VAR(func->m_args[def_arg]); + if(var->m_symbolic_value == nullptr) { + missed_args_names+= "'" + std::string(var->m_name) + "' and "; + missed_args_count++; + } else { + ASR::call_arg_t call_arg; + call_arg.m_value = var->m_symbolic_value; + call_arg.loc = (var->m_symbolic_value->base).loc; + args.push_back(al,call_arg); + } + } + if(missed_args_count > 0){ + missed_args_names = missed_args_names.substr(0,missed_args_names.length() - 5); + diag.add(diag::Diagnostic( + "Number of arguments does not match in the function call", + diag::Level::Error, diag::Stage::Semantic, { + diag::Label("missing " + std::to_string(missed_args_count) + " required arguments :" + missed_args_names, + {loc}) + }) + ); + throw SemanticAbort(); + } } if (ASRUtils::get_FunctionType(func)->m_is_restriction) { rt_vec.push_back(s); @@ -4269,6 +4320,7 @@ class SymbolTableVisitor : public CommonVisitor { throw SemanticError("Function " + std::string(x.m_name) + " is already defined", x.base.base.loc); } bool is_allocatable = false, is_const = false; + size_t default_arg_index_start = x.m_args.n_args - x.m_args.n_defaults; for (size_t i=0; i { std::string arg_s = arg; ASR::expr_t *value = nullptr; ASR::expr_t *init_expr = nullptr; + if (i >= default_arg_index_start){ + size_t default_arg_index = i - default_arg_index_start; + this->visit_expr(*(x.m_args.m_defaults[default_arg_index])); + init_expr = ASRUtils::EXPR(tmp); + } if (s_intent == ASRUtils::intent_unspecified) { s_intent = ASRUtils::intent_in; if (ASRUtils::is_array(arg_type)) { @@ -4308,6 +4365,9 @@ class SymbolTableVisitor : public CommonVisitor { } ASR::accessType s_access = ASR::accessType::Public; ASR::presenceType s_presence = ASR::presenceType::Required; + if (i >= default_arg_index_start){ + s_presence = ASR::presenceType::Optional; + } bool value_attr = false; if (current_procedure_abi_type == ASR::abiType::BindC) { value_attr = true; diff --git a/tests/errors/def_func_01.py b/tests/errors/def_func_01.py new file mode 100644 index 0000000000..4611f33a96 --- /dev/null +++ b/tests/errors/def_func_01.py @@ -0,0 +1,5 @@ +from lpython import i32 +def func_01(x : str) -> str: + print(x) + +func_01() \ No newline at end of file diff --git a/tests/errors/def_func_02.py b/tests/errors/def_func_02.py new file mode 100644 index 0000000000..c94e00a5a5 --- /dev/null +++ b/tests/errors/def_func_02.py @@ -0,0 +1,5 @@ +from lpython import i32 +def func_02(x : i32 ,y : i32) -> i32 : + print(x,y) + +func_02() \ No newline at end of file diff --git a/tests/errors/def_func_03.py b/tests/errors/def_func_03.py new file mode 100644 index 0000000000..da885e3c45 --- /dev/null +++ b/tests/errors/def_func_03.py @@ -0,0 +1,5 @@ +from lpython import i32 +def func_03(x : i32 ,y : i32) -> i32 : + print(x,y) + +func_03(1) \ No newline at end of file diff --git a/tests/errors/def_func_04.py b/tests/errors/def_func_04.py new file mode 100644 index 0000000000..f7f9f81d47 --- /dev/null +++ b/tests/errors/def_func_04.py @@ -0,0 +1,5 @@ +from lpython import i32 +def func_04(x : i32 ,y : i32) -> i32 : + print(x,y) + +func_04(y=3) \ No newline at end of file diff --git a/tests/errors/def_func_05.py b/tests/errors/def_func_05.py new file mode 100644 index 0000000000..3fb86d7d7d --- /dev/null +++ b/tests/errors/def_func_05.py @@ -0,0 +1,5 @@ +from lpython import i32 +def func_05(x : i32 ,y : i32,z : i32) -> i32 : + print(x,y,z) + +func_05(1,2) \ No newline at end of file diff --git a/tests/errors/def_func_06.py b/tests/errors/def_func_06.py new file mode 100644 index 0000000000..babf3eb065 --- /dev/null +++ b/tests/errors/def_func_06.py @@ -0,0 +1,5 @@ +from lpython import i32 +def func_05(x : i32 ,y : i32,z : i32) -> i32 : + print(x,y,z) + +func_05(z=3) \ No newline at end of file diff --git a/tests/reference/asr-def_func_01-1c7f4cd.json b/tests/reference/asr-def_func_01-1c7f4cd.json new file mode 100644 index 0000000000..4eff9fff31 --- /dev/null +++ b/tests/reference/asr-def_func_01-1c7f4cd.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-def_func_01-1c7f4cd", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/def_func_01.py", + "infile_hash": "fda645ec7920824250cc2b5c28663061fe629b1dc341fc70ba3a691c", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-def_func_01-1c7f4cd.stderr", + "stderr_hash": "e96fc67b26c68ef0954595fb87bf261a1bfe6e9f55d83baf28e73032", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-def_func_01-1c7f4cd.stderr b/tests/reference/asr-def_func_01-1c7f4cd.stderr new file mode 100644 index 0000000000..ac8d574cb7 --- /dev/null +++ b/tests/reference/asr-def_func_01-1c7f4cd.stderr @@ -0,0 +1,5 @@ +semantic error: Number of arguments does not match in the function call + --> tests/errors/def_func_01.py:5:1 + | +5 | func_01() + | ^^^^^^^^^ missing 1 required arguments : 'x' diff --git a/tests/reference/asr-def_func_02-8bf7092.json b/tests/reference/asr-def_func_02-8bf7092.json new file mode 100644 index 0000000000..dd639549f4 --- /dev/null +++ b/tests/reference/asr-def_func_02-8bf7092.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-def_func_02-8bf7092", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/def_func_02.py", + "infile_hash": "fe3a3789ece86f790691ead17887dfebb8d60b882f58e06d333c9bb2", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-def_func_02-8bf7092.stderr", + "stderr_hash": "61aea2e70bfee634a40291abc98afa838c6ca173201d9d16f9dfb428", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-def_func_02-8bf7092.stderr b/tests/reference/asr-def_func_02-8bf7092.stderr new file mode 100644 index 0000000000..7c4bcd5d23 --- /dev/null +++ b/tests/reference/asr-def_func_02-8bf7092.stderr @@ -0,0 +1,5 @@ +semantic error: Number of arguments does not match in the function call + --> tests/errors/def_func_02.py:5:1 + | +5 | func_02() + | ^^^^^^^^^ missing 2 required arguments : 'x' and 'y' diff --git a/tests/reference/asr-def_func_03-58ad7c5.json b/tests/reference/asr-def_func_03-58ad7c5.json new file mode 100644 index 0000000000..d702aeffdf --- /dev/null +++ b/tests/reference/asr-def_func_03-58ad7c5.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-def_func_03-58ad7c5", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/def_func_03.py", + "infile_hash": "e69f130e474a8757368e7ad3e9afcd3553eaff1e819173febb66fd06", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-def_func_03-58ad7c5.stderr", + "stderr_hash": "5ac45e87ffbe795b9ca06dc4a82d3743c762f4f0a1f6bbfdc3e14ca2", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-def_func_03-58ad7c5.stderr b/tests/reference/asr-def_func_03-58ad7c5.stderr new file mode 100644 index 0000000000..3c357d9a50 --- /dev/null +++ b/tests/reference/asr-def_func_03-58ad7c5.stderr @@ -0,0 +1,5 @@ +semantic error: Number of arguments does not match in the function call + --> tests/errors/def_func_03.py:5:1 + | +5 | func_03(1) + | ^^^^^^^^^^ missing 1 required arguments : 'y' diff --git a/tests/reference/asr-def_func_04-4af8c0d.json b/tests/reference/asr-def_func_04-4af8c0d.json new file mode 100644 index 0000000000..51c9bf2948 --- /dev/null +++ b/tests/reference/asr-def_func_04-4af8c0d.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-def_func_04-4af8c0d", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/def_func_04.py", + "infile_hash": "522166d0c6c1a0cb273d67ce577ec42330f02822c75b1b317c97608c", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-def_func_04-4af8c0d.stderr", + "stderr_hash": "11bd3ae2f41227fd383927fa2f9fc4feff50c19784df51b56f50d3e9", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-def_func_04-4af8c0d.stderr b/tests/reference/asr-def_func_04-4af8c0d.stderr new file mode 100644 index 0000000000..88195b0527 --- /dev/null +++ b/tests/reference/asr-def_func_04-4af8c0d.stderr @@ -0,0 +1,5 @@ +semantic error: Number of arguments does not match in the function call + --> tests/errors/def_func_04.py:5:1 + | +5 | func_04(y=3) + | ^^^^^^^^^^^^ missing 1 required arguments :'x' diff --git a/tests/reference/asr-def_func_05-6c33b29.json b/tests/reference/asr-def_func_05-6c33b29.json new file mode 100644 index 0000000000..68c9f7192a --- /dev/null +++ b/tests/reference/asr-def_func_05-6c33b29.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-def_func_05-6c33b29", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/def_func_05.py", + "infile_hash": "bc8d5377ec564a4d5758653dea39d5c6237992a54ec33fdef88f63f2", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-def_func_05-6c33b29.stderr", + "stderr_hash": "9dad35128e5da8dcc73f9c96bdb43ce15e3309d590bb794b14e3133c", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-def_func_05-6c33b29.stderr b/tests/reference/asr-def_func_05-6c33b29.stderr new file mode 100644 index 0000000000..4af8d9f66c --- /dev/null +++ b/tests/reference/asr-def_func_05-6c33b29.stderr @@ -0,0 +1,5 @@ +semantic error: Number of arguments does not match in the function call + --> tests/errors/def_func_05.py:5:1 + | +5 | func_05(1,2) + | ^^^^^^^^^^^^ missing 1 required arguments : 'z' diff --git a/tests/reference/asr-def_func_06-9a3ebfc.json b/tests/reference/asr-def_func_06-9a3ebfc.json new file mode 100644 index 0000000000..77f6bfe11b --- /dev/null +++ b/tests/reference/asr-def_func_06-9a3ebfc.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-def_func_06-9a3ebfc", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/def_func_06.py", + "infile_hash": "5ad73c3f18ab4d9fe82108e65e0013687a70acc3eff495a402d4297e", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-def_func_06-9a3ebfc.stderr", + "stderr_hash": "f9c79e62d7ef7f411870195bfeb99615cb7da9216af328fda2fb0cd2", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-def_func_06-9a3ebfc.stderr b/tests/reference/asr-def_func_06-9a3ebfc.stderr new file mode 100644 index 0000000000..65527b826a --- /dev/null +++ b/tests/reference/asr-def_func_06-9a3ebfc.stderr @@ -0,0 +1,5 @@ +semantic error: Number of arguments does not match in the function call + --> tests/errors/def_func_06.py:5:1 + | +5 | func_05(z=3) + | ^^^^^^^^^^^^ missing 2 required arguments :'x' and 'y' diff --git a/tests/tests.toml b/tests/tests.toml index b106996999..7e9b1d43d0 100644 --- a/tests/tests.toml +++ b/tests/tests.toml @@ -1442,4 +1442,28 @@ run_with_dbg = true [[test]] filename = "errors/test_optional.py" +asr = true + +[[test]] +filename = "errors/def_func_01.py" +asr = true + +[[test]] +filename = "errors/def_func_02.py" +asr = true + +[[test]] +filename = "errors/def_func_03.py" +asr = true + +[[test]] +filename = "errors/def_func_04.py" +asr = true + +[[test]] +filename = "errors/def_func_05.py" +asr = true + +[[test]] +filename = "errors/def_func_06.py" asr = true \ No newline at end of file