lcompilers / lpython

Python compiler
https://lpython.org/
Other
1.5k stars 162 forks source link

Improper mutable list in dataclass initializer should error, not throw #2031

Open rebcabin opened 1 year ago

rebcabin commented 1 year ago

offending code:

from lpython import dataclass, i32

@dataclass
class foo:
    bar : list[i32] = []  ######### INCORRECT ###########

run:


# CPYTHON

(lp) ┌─(~/CLionProjects/lpython/lasr/LP-pycharm/Issue2031)───────────────────────────────────────────────────────────────────────────────────────────────(brian@Golf37:s012)─┐
└─(16:52:15 on brian-lasr ✹ ✚ ✭)──> PYTHONPATH='../../../src/runtime/lpython' python issue2031.py                                                         ──(Sun,Jun25)─┘
Traceback (most recent call last):
  File "/Users/brian/CLionProjects/lpython/lasr/LP-pycharm/Issue2031/issue2031.py", line 4, in <module>
    class foo:
  File "/Users/brian/CLionProjects/lpython/src/runtime/lpython/lpython.py", line 55, in dataclass
    return py_dataclass(arg)
  File "/Users/brian/mambaforge/envs/lp/lib/python3.10/dataclasses.py", line 1185, in dataclass
    return wrap(cls)
  File "/Users/brian/mambaforge/envs/lp/lib/python3.10/dataclasses.py", line 1176, in wrap
    return _process_class(cls, init, repr, eq, order, unsafe_hash,
  File "/Users/brian/mambaforge/envs/lp/lib/python3.10/dataclasses.py", line 956, in _process_class
    cls_fields.append(_get_field(cls, name, type, kw_only))
  File "/Users/brian/mambaforge/envs/lp/lib/python3.10/dataclasses.py", line 813, in _get_field
    raise ValueError(f'mutable default {type(f.default)} for field '
ValueError: mutable default <class 'list'> for field bar is not allowed: use default_factory

# LPYTHON

(lp) ┌─(~/CLionProjects/lpython/lasr/LP-pycharm/Issue2031)───────────────────────────────────────────────────────────────────────────────────────────────(brian@Golf37:s012)─┐
└─(16:52:22 on brian-lasr ✹ ✚ ✭)──> ~/CLionProjects/lpython/src/bin/lpython -I. issue2031.py                                                          1 ↵ ──(Sun,Jun25)─┘
Internal Compiler Error: Unhandled exception
Traceback (most recent call last):
  File "/Users/brian/CLionProjects/lpython/src/bin/lpython.cpp", line 1844
    err = compile_python_to_object_file(arg_file, tmp_o, runtime_library_dir,
  File "/Users/brian/CLionProjects/lpython/src/bin/lpython.cpp", line 783
    r1 = LCompilers::LPython::python_ast_to_asr(al, lm, *ast, diagnostics, compiler_options,
  File "/Users/brian/CLionProjects/lpython/src/lpython/semantics/python_ast_to_asr.cpp", line 7520
    auto res = symbol_table_visitor(al, lm, *ast_m, diagnostics, main_module,
  File "/Users/brian/CLionProjects/lpython/src/lpython/semantics/python_ast_to_asr.cpp", line 4542
    v.visit_Module(ast);
  File "/Users/brian/CLionProjects/lpython/src/lpython/semantics/python_ast_to_asr.cpp", line 3865
    visit_stmt(*x.m_body[i]);
  File "/Users/brian/CLionProjects/lpython/src/lpython/python_ast.h", line 1883
    void visit_stmt(const stmt_t &b) { visit_stmt_t(b, self()); }
  File "/Users/brian/CLionProjects/lpython/src/lpython/python_ast.h", line 1752
    case stmtType::AsyncFunctionDef: { v.visit_AsyncFunctionDef((const AsyncFunctionDef_t &)x); return; }
  File "/Users/brian/CLionProjects/lpython/src/lpython/semantics/python_ast_to_asr.cpp", line 3096
    visit_ClassMembers(x, member_names, struct_dependencies, false, class_abi);
  File "/Users/brian/CLionProjects/lpython/src/lpython/semantics/python_ast_to_asr.cpp", line 2900
    visit_AnnAssignUtil(*ann_assign, var_name, false, init_expr, abi);
  File "/Users/brian/CLionProjects/lpython/src/lpython/semantics/python_ast_to_asr.cpp", line 2799
    this->visit_expr(*x.m_value);
  File "/Users/brian/CLionProjects/lpython/src/lpython/python_ast.h", line 1910
    void visit_expr(const expr_t &b) { visit_expr_t(b, self()); }
  File "/Users/brian/CLionProjects/lpython/src/lpython/python_ast.h", line 1814
    case exprType::Name: { v.visit_Name((const Name_t &)x); return; }
  File "/Users/brian/CLionProjects/lpython/src/lpython/python_ast.h", line 1942
    void visit_List(const List_t & /* x */) { throw LCompilersException("visit_List() not implemented"); }
LCompilersException: visit_List() not implemented
rebcabin commented 1 year ago

Looks like we DO have field and default_factory ! GREAT!

aryangupta701 commented 7 months ago

can you assign this issue to me?

aryangupta701 commented 7 months ago

I need a little help here to work with. I went a little through the python_ast_to_asr.cpp file and I am think I need to handle the error here instead of throwing. I am not sure about this please confirm

 void visit_List(const AST::List_t &x) {
        Vec<ASR::expr_t*> list;
        list.reserve(al, x.n_elts + 1);
        ASR::ttype_t *type = nullptr;
        ASR::expr_t *expr = nullptr;
        if( x.n_elts > 0 ) {
            this->visit_expr(*x.m_elts[0]);
            expr = ASRUtils::EXPR(tmp);
            type = ASRUtils::expr_type(expr);
            list.push_back(al, expr);
            for (size_t i = 1; i < x.n_elts; i++) {
                this->visit_expr(*x.m_elts[i]);
                expr = ASRUtils::EXPR(tmp);
                if (!ASRUtils::check_equal_type(ASRUtils::expr_type(expr), type)) {
                    // Handle the error instead of throwing ? 
                    throw SemanticError("All List elements must be of the same type for now",
                        x.base.base.loc);
                }
                list.push_back(al, expr);
            }
        } else {
            if( assign_asr_target == nullptr ) {
                tmp = nullptr;
                return ;
            }
            type = ASRUtils::get_contained_type(
                ASRUtils::type_get_past_const(ASRUtils::expr_type(assign_asr_target)));
        }
        ASR::ttype_t* list_type = ASRUtils::TYPE(ASR::make_List_t(al, x.base.base.loc, type));
        tmp = ASR::make_ListConstant_t(al, x.base.base.loc, list.p,
            list.size(), list_type);
    }
aryangupta701 commented 7 months ago

Also how can I compile lpython code?

I want to reproduce the output for this code locally

from lpython import dataclass, i32

@dataclass
class foo:
    bar : list[i32] = []  ######### INCORRECT ###########