diff --git a/src/libasr/ASR.asdl b/src/libasr/ASR.asdl index 9555c7d585..28326cba86 100644 --- a/src/libasr/ASR.asdl +++ b/src/libasr/ASR.asdl @@ -253,6 +253,7 @@ expr | Cast(expr arg, cast_kind kind, ttype type, expr? value) | ComplexRe(expr arg, ttype type, expr? value) | ComplexIm(expr arg, ttype type, expr? value) + | DictItem(symbol a, expr key, expr? default, ttype type) -- `len` in Character: diff --git a/src/libasr/asr_utils.h b/src/libasr/asr_utils.h index ca4c5ffa8b..fbcd18f502 100644 --- a/src/libasr/asr_utils.h +++ b/src/libasr/asr_utils.h @@ -128,6 +128,7 @@ static inline ASR::ttype_t* expr_type(const ASR::expr_t *f) case ASR::exprType::Cast: { return ((ASR::Cast_t*)f)->m_type; } case ASR::exprType::ComplexRe: { return ((ASR::ComplexRe_t*)f)->m_type; } case ASR::exprType::ComplexIm: { return ((ASR::ComplexIm_t*)f)->m_type; } + case ASR::exprType::DictItem: { return ((ASR::DictItem_t*)f)->m_type; } default : throw LFortranException("Not implemented"); } } @@ -284,6 +285,7 @@ static inline ASR::expr_t* expr_value(ASR::expr_t *f) case ASR::exprType::SetLen: { return ASR::down_cast(f)->m_value; } case ASR::exprType::ComplexRe: { return ASR::down_cast(f)->m_value; } case ASR::exprType::ComplexIm: { return ASR::down_cast(f)->m_value; } + case ASR::exprType::DictItem: // Drop through case ASR::exprType::ArrayConstant: // Drop through case ASR::exprType::IntegerConstant: // Drop through case ASR::exprType::RealConstant: // Drop through diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index eac5ea628a..afa5b40a95 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -2814,6 +2814,53 @@ class BodyVisitor : public CommonVisitor { SymbolTable *symtab = current_scope; while (symtab->parent != nullptr) symtab = symtab->parent; if (symtab->scope.find(mod_name) == symtab->scope.end()) { + if (current_scope->scope.find(mod_name) != current_scope->scope.end()) { + // this case when we have variable and attribute + st = current_scope->scope[mod_name]; + ASR::Variable_t *v = ASR::down_cast(st); + ASR::ttype_t *type = v->m_type; + if (ASR::is_a(*type)) { + if (call_name == "get") { + ASR::expr_t *def = nullptr; + if (args.size() > 2 || args.size() < 1) { + throw SemanticError("'get' takes atleast 1 and atmost 2 arguments", + x.base.base.loc); + } + ASR::ttype_t *key_type = ASR::down_cast(type)->m_key_type; + ASR::ttype_t *value_type = ASR::down_cast(type)->m_value_type; + if (args.size() == 2) { + def = args[1].m_value; + if (!ASRUtils::check_equal_type(ASRUtils::expr_type(def), value_type)) { + std::string vtype = ASRUtils::type_to_str_python(ASRUtils::expr_type(def)); + std::string totype = ASRUtils::type_to_str_python(value_type); + diag.add(diag::Diagnostic( + "Type mismatch in get's default value, the types must be compatible", + diag::Level::Error, diag::Stage::Semantic, { + diag::Label("type mismatch (found: '" + vtype + "', expected: '" + totype + "')", + {def->base.loc}) + }) + ); + throw SemanticAbort(); + } + } + if (!ASRUtils::check_equal_type(ASRUtils::expr_type(args[0].m_value), key_type)) { + std::string ktype = ASRUtils::type_to_str_python(ASRUtils::expr_type(args[0].m_value)); + std::string totype = ASRUtils::type_to_str_python(key_type); + diag.add(diag::Diagnostic( + "Type mismatch in get's key value, the types must be compatible", + diag::Level::Error, diag::Stage::Semantic, { + diag::Label("type mismatch (found: '" + ktype + "', expected: '" + totype + "')", + {args[0].m_value->base.loc}) + }) + ); + throw SemanticAbort(); + } + tmp = make_DictItem_t(al, x.base.base.loc, st, args[0].m_value, def, + value_type); + return; + } + } + } throw SemanticError("module '" + mod_name + "' is not imported", x.base.base.loc); } diff --git a/tests/dictionary1.py b/tests/dictionary1.py index 0a9a2b530b..ffa5465bfb 100644 --- a/tests/dictionary1.py +++ b/tests/dictionary1.py @@ -16,3 +16,11 @@ def test_dict_insert(): y: dict[str, i32] y = {"a": -1, "b": -2} y["c"] = -3 + + +def test_dict_get(): + y: dict[str, i32] + y = {"a": -1, "b": -2} + x: i32 + x = y.get("a") + x = y.get("a", 0) diff --git a/tests/errors/test_dict3.py b/tests/errors/test_dict3.py new file mode 100644 index 0000000000..fbc622e2a2 --- /dev/null +++ b/tests/errors/test_dict3.py @@ -0,0 +1,5 @@ +def test_e1(): + y: dict[str, i32] + y = {"a": -1, "b": -2} + x: i32 + x = y.get(1) diff --git a/tests/reference/asr-dictionary1-a105a36.json b/tests/reference/asr-dictionary1-a105a36.json index 29d920072b..67fec1472b 100644 --- a/tests/reference/asr-dictionary1-a105a36.json +++ b/tests/reference/asr-dictionary1-a105a36.json @@ -2,11 +2,11 @@ "basename": "asr-dictionary1-a105a36", "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", "infile": "tests/dictionary1.py", - "infile_hash": "de60b11cae2fca92d379d49e16dff8e844880a8d399d5ce663fef743", + "infile_hash": "d24f9165c2fb6f0441d774936dc2dfce99a0fec7225713607806a19a", "outfile": null, "outfile_hash": null, "stdout": "asr-dictionary1-a105a36.stdout", - "stdout_hash": "01abb24628bd259575ffe0e0290ef41bdd4c8899fb8ca4e9d35af443", + "stdout_hash": "e7bc564ba9f0544a67c0ff05f0825f70f8e56872521068389dd9134e", "stderr": null, "stderr_hash": null, "returncode": 0 diff --git a/tests/reference/asr-dictionary1-a105a36.stdout b/tests/reference/asr-dictionary1-a105a36.stdout index 06a7ff1f05..f38e8e2d4e 100644 --- a/tests/reference/asr-dictionary1-a105a36.stdout +++ b/tests/reference/asr-dictionary1-a105a36.stdout @@ -1 +1 @@ -(TranslationUnit (SymbolTable 1 {main_program: (Program (SymbolTable 4 {}) main_program [] []), test_Dict: (Subroutine (SymbolTable 2 {x: (Variable 2 x Local () () Default (Dict (Integer 4 []) (Integer 4 [])) Source Public Required .false.), y: (Variable 2 y Local () () Default (Dict (Character 1 -2 () []) (Integer 4 [])) Source Public Required .false.), z: (Variable 2 z Local () () Default (Integer 4 []) Source Public Required .false.)}) test_Dict [] [(= (Var 2 x) (DictConstant [(IntegerConstant 1 (Integer 4 [])) (IntegerConstant 3 (Integer 4 []))] [(IntegerConstant 2 (Integer 4 [])) (IntegerConstant 4 (Integer 4 []))] (Dict (Integer 4 []) (Integer 4 []))) ()) (= (Var 2 y) (DictConstant [(StringConstant "a" (Character 1 1 () [])) (StringConstant "b" (Character 1 1 () []))] [(UnaryOp USub (IntegerConstant 1 (Integer 4 [])) (Integer 4 []) (IntegerConstant -1 (Integer 4 []))) (UnaryOp USub (IntegerConstant 2 (Integer 4 [])) (Integer 4 []) (IntegerConstant -2 (Integer 4 [])))] (Dict (Character 1 1 () []) (Integer 4 []))) ()) (= (Var 2 z) (ArrayRef 2 y [(() (StringConstant "a" (Character 1 1 () [])) ())] (Integer 4 []) ()) ()) (= (Var 2 z) (ArrayRef 2 y [(() (StringConstant "b" (Character 1 1 () [])) ())] (Integer 4 []) ()) ()) (= (Var 2 z) (ArrayRef 2 x [(() (IntegerConstant 1 (Integer 4 [])) ())] (Integer 4 []) ()) ())] Source Public Implementation () .false. .false.), test_dict_insert: (Subroutine (SymbolTable 3 {y: (Variable 3 y Local () () Default (Dict (Character 1 -2 () []) (Integer 4 [])) Source Public Required .false.)}) test_dict_insert [] [(= (Var 3 y) (DictConstant [(StringConstant "a" (Character 1 1 () [])) (StringConstant "b" (Character 1 1 () []))] [(UnaryOp USub (IntegerConstant 1 (Integer 4 [])) (Integer 4 []) (IntegerConstant -1 (Integer 4 []))) (UnaryOp USub (IntegerConstant 2 (Integer 4 [])) (Integer 4 []) (IntegerConstant -2 (Integer 4 [])))] (Dict (Character 1 1 () []) (Integer 4 []))) ()) (DictInsert 3 y (StringConstant "c" (Character 1 1 () [])) (UnaryOp USub (IntegerConstant 3 (Integer 4 [])) (Integer 4 []) (IntegerConstant -3 (Integer 4 []))))] Source Public Implementation () .false. .false.)}) []) +(TranslationUnit (SymbolTable 1 {main_program: (Program (SymbolTable 5 {}) main_program [] []), test_Dict: (Subroutine (SymbolTable 2 {x: (Variable 2 x Local () () Default (Dict (Integer 4 []) (Integer 4 [])) Source Public Required .false.), y: (Variable 2 y Local () () Default (Dict (Character 1 -2 () []) (Integer 4 [])) Source Public Required .false.), z: (Variable 2 z Local () () Default (Integer 4 []) Source Public Required .false.)}) test_Dict [] [(= (Var 2 x) (DictConstant [(IntegerConstant 1 (Integer 4 [])) (IntegerConstant 3 (Integer 4 []))] [(IntegerConstant 2 (Integer 4 [])) (IntegerConstant 4 (Integer 4 []))] (Dict (Integer 4 []) (Integer 4 []))) ()) (= (Var 2 y) (DictConstant [(StringConstant "a" (Character 1 1 () [])) (StringConstant "b" (Character 1 1 () []))] [(UnaryOp USub (IntegerConstant 1 (Integer 4 [])) (Integer 4 []) (IntegerConstant -1 (Integer 4 []))) (UnaryOp USub (IntegerConstant 2 (Integer 4 [])) (Integer 4 []) (IntegerConstant -2 (Integer 4 [])))] (Dict (Character 1 1 () []) (Integer 4 []))) ()) (= (Var 2 z) (ArrayRef 2 y [(() (StringConstant "a" (Character 1 1 () [])) ())] (Integer 4 []) ()) ()) (= (Var 2 z) (ArrayRef 2 y [(() (StringConstant "b" (Character 1 1 () [])) ())] (Integer 4 []) ()) ()) (= (Var 2 z) (ArrayRef 2 x [(() (IntegerConstant 1 (Integer 4 [])) ())] (Integer 4 []) ()) ())] Source Public Implementation () .false. .false.), test_dict_get: (Subroutine (SymbolTable 4 {x: (Variable 4 x Local () () Default (Integer 4 []) Source Public Required .false.), y: (Variable 4 y Local () () Default (Dict (Character 1 -2 () []) (Integer 4 [])) Source Public Required .false.)}) test_dict_get [] [(= (Var 4 y) (DictConstant [(StringConstant "a" (Character 1 1 () [])) (StringConstant "b" (Character 1 1 () []))] [(UnaryOp USub (IntegerConstant 1 (Integer 4 [])) (Integer 4 []) (IntegerConstant -1 (Integer 4 []))) (UnaryOp USub (IntegerConstant 2 (Integer 4 [])) (Integer 4 []) (IntegerConstant -2 (Integer 4 [])))] (Dict (Character 1 1 () []) (Integer 4 []))) ()) (= (Var 4 x) (DictItem 4 y (StringConstant "a" (Character 1 1 () [])) () (Integer 4 [])) ()) (= (Var 4 x) (DictItem 4 y (StringConstant "a" (Character 1 1 () [])) (IntegerConstant 0 (Integer 4 [])) (Integer 4 [])) ())] Source Public Implementation () .false. .false.), test_dict_insert: (Subroutine (SymbolTable 3 {y: (Variable 3 y Local () () Default (Dict (Character 1 -2 () []) (Integer 4 [])) Source Public Required .false.)}) test_dict_insert [] [(= (Var 3 y) (DictConstant [(StringConstant "a" (Character 1 1 () [])) (StringConstant "b" (Character 1 1 () []))] [(UnaryOp USub (IntegerConstant 1 (Integer 4 [])) (Integer 4 []) (IntegerConstant -1 (Integer 4 []))) (UnaryOp USub (IntegerConstant 2 (Integer 4 [])) (Integer 4 []) (IntegerConstant -2 (Integer 4 [])))] (Dict (Character 1 1 () []) (Integer 4 []))) ()) (DictInsert 3 y (StringConstant "c" (Character 1 1 () [])) (UnaryOp USub (IntegerConstant 3 (Integer 4 [])) (Integer 4 []) (IntegerConstant -3 (Integer 4 []))))] Source Public Implementation () .false. .false.)}) []) diff --git a/tests/reference/asr-test_dict3-d28f38f.json b/tests/reference/asr-test_dict3-d28f38f.json new file mode 100644 index 0000000000..ea944408ea --- /dev/null +++ b/tests/reference/asr-test_dict3-d28f38f.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-test_dict3-d28f38f", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/test_dict3.py", + "infile_hash": "c8930919a9c47244ed4c57c1235dd2000650eb61dbfb6f413ae934e0", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-test_dict3-d28f38f.stderr", + "stderr_hash": "e2cc26634c1ee7aeca96c006b3bd01205200e1a6187e01fde041c3b4", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-test_dict3-d28f38f.stderr b/tests/reference/asr-test_dict3-d28f38f.stderr new file mode 100644 index 0000000000..da5a97b2ce --- /dev/null +++ b/tests/reference/asr-test_dict3-d28f38f.stderr @@ -0,0 +1,5 @@ +semantic error: Type mismatch in get's key value, the types must be compatible + --> tests/errors/test_dict3.py:5:15 + | +5 | x = y.get(1) + | ^ type mismatch (found: 'i32', expected: 'str') diff --git a/tests/reference/ast-dictionary1-1a7e00a.json b/tests/reference/ast-dictionary1-1a7e00a.json index 51fa9f187e..db449de2b1 100644 --- a/tests/reference/ast-dictionary1-1a7e00a.json +++ b/tests/reference/ast-dictionary1-1a7e00a.json @@ -2,11 +2,11 @@ "basename": "ast-dictionary1-1a7e00a", "cmd": "lpython --show-ast --no-color {infile} -o {outfile}", "infile": "tests/dictionary1.py", - "infile_hash": "de60b11cae2fca92d379d49e16dff8e844880a8d399d5ce663fef743", + "infile_hash": "d24f9165c2fb6f0441d774936dc2dfce99a0fec7225713607806a19a", "outfile": null, "outfile_hash": null, "stdout": "ast-dictionary1-1a7e00a.stdout", - "stdout_hash": "a1ee1abcc12edf02f1947e80709e5515366025a7c13744b682e87fa9", + "stdout_hash": "a4d907b481d700a454753881457ddb21264620b417ab442221241e1b", "stderr": null, "stderr_hash": null, "returncode": 0 diff --git a/tests/reference/ast-dictionary1-1a7e00a.stdout b/tests/reference/ast-dictionary1-1a7e00a.stdout index 544d9c102b..f77e9494cd 100644 --- a/tests/reference/ast-dictionary1-1a7e00a.stdout +++ b/tests/reference/ast-dictionary1-1a7e00a.stdout @@ -1 +1 @@ -(Module [(FunctionDef test_Dict ([] [] [] [] [] [] []) [(AnnAssign (Name x Store) (Subscript (Name dict Load) (Tuple [(Name i32 Load) (Name i32 Load)] Load) Load) () 1) (Assign [(Name x Store)] (Dict [(ConstantInt 1 ()) (ConstantInt 3 ())] [(ConstantInt 2 ()) (ConstantInt 4 ())]) ()) (AnnAssign (Name y Store) (Subscript (Name dict Load) (Tuple [(Name str Load) (Name i32 Load)] Load) Load) () 1) (Assign [(Name y Store)] (Dict [(ConstantStr "a" ()) (ConstantStr "b" ())] [(UnaryOp USub (ConstantInt 1 ())) (UnaryOp USub (ConstantInt 2 ()))]) ()) (AnnAssign (Name z Store) (Name i32 Load) () 1) (Assign [(Name z Store)] (Subscript (Name y Load) (ConstantStr "a" ()) Load) ()) (Assign [(Name z Store)] (Subscript (Name y Load) (ConstantStr "b" ()) Load) ()) (Assign [(Name z Store)] (Subscript (Name x Load) (ConstantInt 1 ()) Load) ())] [] () ()) (FunctionDef test_dict_insert ([] [] [] [] [] [] []) [(AnnAssign (Name y Store) (Subscript (Name dict Load) (Tuple [(Name str Load) (Name i32 Load)] Load) Load) () 1) (Assign [(Name y Store)] (Dict [(ConstantStr "a" ()) (ConstantStr "b" ())] [(UnaryOp USub (ConstantInt 1 ())) (UnaryOp USub (ConstantInt 2 ()))]) ()) (Assign [(Subscript (Name y Load) (ConstantStr "c" ()) Store)] (UnaryOp USub (ConstantInt 3 ())) ())] [] () ())] []) +(Module [(FunctionDef test_Dict ([] [] [] [] [] [] []) [(AnnAssign (Name x Store) (Subscript (Name dict Load) (Tuple [(Name i32 Load) (Name i32 Load)] Load) Load) () 1) (Assign [(Name x Store)] (Dict [(ConstantInt 1 ()) (ConstantInt 3 ())] [(ConstantInt 2 ()) (ConstantInt 4 ())]) ()) (AnnAssign (Name y Store) (Subscript (Name dict Load) (Tuple [(Name str Load) (Name i32 Load)] Load) Load) () 1) (Assign [(Name y Store)] (Dict [(ConstantStr "a" ()) (ConstantStr "b" ())] [(UnaryOp USub (ConstantInt 1 ())) (UnaryOp USub (ConstantInt 2 ()))]) ()) (AnnAssign (Name z Store) (Name i32 Load) () 1) (Assign [(Name z Store)] (Subscript (Name y Load) (ConstantStr "a" ()) Load) ()) (Assign [(Name z Store)] (Subscript (Name y Load) (ConstantStr "b" ()) Load) ()) (Assign [(Name z Store)] (Subscript (Name x Load) (ConstantInt 1 ()) Load) ())] [] () ()) (FunctionDef test_dict_insert ([] [] [] [] [] [] []) [(AnnAssign (Name y Store) (Subscript (Name dict Load) (Tuple [(Name str Load) (Name i32 Load)] Load) Load) () 1) (Assign [(Name y Store)] (Dict [(ConstantStr "a" ()) (ConstantStr "b" ())] [(UnaryOp USub (ConstantInt 1 ())) (UnaryOp USub (ConstantInt 2 ()))]) ()) (Assign [(Subscript (Name y Load) (ConstantStr "c" ()) Store)] (UnaryOp USub (ConstantInt 3 ())) ())] [] () ()) (FunctionDef test_dict_get ([] [] [] [] [] [] []) [(AnnAssign (Name y Store) (Subscript (Name dict Load) (Tuple [(Name str Load) (Name i32 Load)] Load) Load) () 1) (Assign [(Name y Store)] (Dict [(ConstantStr "a" ()) (ConstantStr "b" ())] [(UnaryOp USub (ConstantInt 1 ())) (UnaryOp USub (ConstantInt 2 ()))]) ()) (AnnAssign (Name x Store) (Name i32 Load) () 1) (Assign [(Name x Store)] (Call (Attribute (Name y Load) get Load) [(ConstantStr "a" ())] []) ()) (Assign [(Name x Store)] (Call (Attribute (Name y Load) get Load) [(ConstantStr "a" ()) (ConstantInt 0 ())] []) ())] [] () ())] []) diff --git a/tests/tests.toml b/tests/tests.toml index f625ab54cb..761a1a3b53 100644 --- a/tests/tests.toml +++ b/tests/tests.toml @@ -377,4 +377,8 @@ asr = true [[test]] filename = "errors/test_dict2.py" -asr = true \ No newline at end of file +asr = true + +[[test]] +filename = "errors/test_dict3.py" +asr = true