diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 5ad8a0074d..ff73dbd74e 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -843,6 +843,7 @@ RUN(NAME class_03 LABELS cpython llvm llvm_jit) RUN(NAME class_04 LABELS cpython llvm llvm_jit) RUN(NAME class_05 LABELS cpython llvm llvm_jit) RUN(NAME class_06 LABELS cpython llvm llvm_jit) +RUN(NAME class_07 LABELS cpython llvm llvm_jit) # callback_04 is to test emulation. So just run with cpython diff --git a/integration_tests/class_07.py b/integration_tests/class_07.py new file mode 100644 index 0000000000..36a0b695e7 --- /dev/null +++ b/integration_tests/class_07.py @@ -0,0 +1,17 @@ +from lpython import i32 + +class MyClass: + class_var: i32 = 42 + + def __init__(self: "MyClass", instance_value: i32): + self.instance_var: i32 = instance_value + +def main(): + assert MyClass.class_var == 42 + MyClass.class_var = 100 + assert MyClass.class_var == 100 + obj1: MyClass = MyClass(10) + assert obj1.class_var == 100 + +if __name__ == '__main__': + main() diff --git a/src/libasr/ASR.asdl b/src/libasr/ASR.asdl index 5ea9a482b5..809846ae04 100644 --- a/src/libasr/ASR.asdl +++ b/src/libasr/ASR.asdl @@ -218,7 +218,7 @@ ttype cast_kind = RealToInteger | IntegerToReal | LogicalToReal | RealToReal | IntegerToInteger | RealToComplex | IntegerToComplex | IntegerToLogical | RealToLogical | CharacterToLogical | CharacterToInteger | CharacterToList | ComplexToLogical | ComplexToComplex | ComplexToReal | ComplexToInteger | LogicalToInteger | RealToCharacter | IntegerToCharacter | LogicalToCharacter | UnsignedIntegerToInteger | UnsignedIntegerToUnsignedInteger | UnsignedIntegerToReal | UnsignedIntegerToLogical | IntegerToUnsignedInteger | RealToUnsignedInteger | CPtrToUnsignedInteger | UnsignedIntegerToCPtr | IntegerToSymbolicExpression | ListToArray | DerivedToBase storage_type = Default | Save | Parameter access = Public | Private -intent = Local | In | Out | InOut | ReturnVar | Unspecified +intent = Local | In | Out | InOut | ReturnVar | ClassMember | Unspecified deftype = Implementation | Interface presence = Required | Optional abi = Source | LFortranModule | GFortranModule | BindC | BindPython | BindJS | Interactive | Intrinsic diff --git a/src/libasr/asr_utils.h b/src/libasr/asr_utils.h index 42d64c4c1c..a418812a62 100644 --- a/src/libasr/asr_utils.h +++ b/src/libasr/asr_utils.h @@ -1852,6 +1852,7 @@ const ASR::intentType intent_local=ASR::intentType::Local; // local variable (no const ASR::intentType intent_in =ASR::intentType::In; // dummy argument, intent(in) const ASR::intentType intent_out =ASR::intentType::Out; // dummy argument, intent(out) const ASR::intentType intent_inout=ASR::intentType::InOut; // dummy argument, intent(inout) +const ASR::intentType intent_classmember=ASR::intentType::ClassMember; // class variable const ASR::intentType intent_return_var=ASR::intentType::ReturnVar; // return variable of a function const ASR::intentType intent_unspecified=ASR::intentType::Unspecified; // dummy argument, ambiguous intent diff --git a/src/libasr/asr_verify.cpp b/src/libasr/asr_verify.cpp index 4ebfe83aa2..0e56a03342 100644 --- a/src/libasr/asr_verify.cpp +++ b/src/libasr/asr_verify.cpp @@ -797,7 +797,7 @@ class VerifyVisitor : public BaseWalkVisitor s = ASRUtils::symbol_get_past_external(x.m_v); } require(is_a(*s) || is_a(*s) - || is_a(*s) || is_a(*s), + || is_a(*s) || is_a(*s) || is_a(*s), "Var_t::m_v " + x_mv_name + " does not point to a Variable_t, " \ "Function_t, or EnumType_t (possibly behind ExternalSymbol_t)"); require(symtab_in_scope(current_symtab, x.m_v), diff --git a/src/libasr/codegen/asr_to_llvm.cpp b/src/libasr/codegen/asr_to_llvm.cpp index ec8a8b0205..c29e76facf 100644 --- a/src/libasr/codegen/asr_to_llvm.cpp +++ b/src/libasr/codegen/asr_to_llvm.cpp @@ -2732,6 +2732,11 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor } ASR::Variable_t* member = down_cast(symbol_get_past_external(x.m_m)); std::string member_name = std::string(member->m_name); + if (member->m_intent == ASRUtils::intent_classmember) { + llvm::Constant *ptr = module->getNamedGlobal(member_name); + tmp = ptr; + return; + } LCOMPILERS_ASSERT(current_der_type_name.size() != 0); while( name2memidx[current_der_type_name].find(member_name) == name2memidx[current_der_type_name].end() ) { if( dertype2parent.find(current_der_type_name) == dertype2parent.end() ) { @@ -2763,6 +2768,24 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor } } + + void visit_StructStaticMember(const ASR::StructStaticMember_t& x) { + if (x.m_value) { + this->visit_expr_wrapper(x.m_value, true); + return; + } + + ASR::Variable_t* member = down_cast(symbol_get_past_external(x.m_m)); + std::string member_name = std::string(member->m_name); + + llvm::Constant *ptr = module->getNamedGlobal(member_name); + if (is_assignment_target) { + tmp = ptr; + return; + } + tmp = CreateLoad(ptr); + } + void visit_Variable(const ASR::Variable_t &x) { if (compiler_options.interactive && std::strcmp(x.m_name, "_") == 0 && @@ -2782,7 +2805,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor // (global variable declared/initialized in this translation unit), or // external (global variable not declared/initialized in this // translation unit, just referenced). - LCOMPILERS_ASSERT(x.m_intent == intent_local || x.m_intent == ASRUtils::intent_unspecified + LCOMPILERS_ASSERT(x.m_intent == intent_local || x.m_intent == ASRUtils::intent_classmember || x.m_intent == ASRUtils::intent_unspecified || x.m_abi == ASR::abiType::Interactive); bool external = (x.m_abi != ASR::abiType::Source); llvm::Constant* init_value = nullptr; @@ -3127,6 +3150,15 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor instantiate_function(*v); } } + for (size_t i = 0; i < x.n_members; i++) { + ASR::Variable_t *v = down_cast(x.m_symtab->get_symbol(x.m_members[i])); + if (v->m_intent == ASRUtils::intent_classmember) { + v->m_name = s2c(al, mangle_prefix + v->m_name); + if (!v->m_symbolic_value) + v->m_symbolic_value = x.m_initializers[i].m_value; + visit_Variable(*v); + } + } current_scope = current_scope_copy; } @@ -4956,6 +4988,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor x.m_target->type == ASR::exprType::StringItem || x.m_target->type == ASR::exprType::ArraySection || x.m_target->type == ASR::exprType::StructInstanceMember || + x.m_target->type == ASR::exprType::StructStaticMember || x.m_target->type == ASR::exprType::ListItem || x.m_target->type == ASR::exprType::DictItem || x.m_target->type == ASR::exprType::UnionInstanceMember ) { diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 63c87dbe4a..e84b16a625 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -2669,9 +2669,9 @@ class CommonVisitor : public AST::BaseVisitor { void create_add_variable_to_scope(std::string& var_name, ASR::ttype_t* type, const Location& loc, ASR::abiType abi, - ASR::storage_typeType storage_type=ASR::storage_typeType::Default) { + ASR::storage_typeType storage_type=ASR::storage_typeType::Default, + ASR::intentType s_intent = ASRUtils::intent_local) { - ASR::intentType s_intent = ASRUtils::intent_local; ASR::abiType current_procedure_abi_type = abi; ASR::accessType s_access = ASR::accessType::Public; ASR::presenceType s_presence = ASR::presenceType::Required; @@ -2891,7 +2891,7 @@ class CommonVisitor : public AST::BaseVisitor { ASR::expr_t* &init_expr, bool wrap_derived_type_in_pointer=false, ASR::abiType abi=ASR::abiType::Source, - bool inside_struct=false) { + bool inside_struct=false, bool inside_class=false) { bool is_allocatable = false, is_const = false; ASR::ttype_t *type = nullptr; if( inside_struct ) { @@ -2917,8 +2917,13 @@ class CommonVisitor : public AST::BaseVisitor { storage_type = ASR::storage_typeType::Parameter; } - create_add_variable_to_scope(var_name, type, - x.base.base.loc, abi, storage_type); + if ( inside_class ) { + create_add_variable_to_scope(var_name, type, + x.base.base.loc, abi, storage_type, ASRUtils::intent_classmember); + } else { + create_add_variable_to_scope(var_name, type, + x.base.base.loc, abi, storage_type); + } ASR::expr_t* assign_asr_target_copy = assign_asr_target; this->visit_expr(*x.m_target); @@ -3082,7 +3087,7 @@ class CommonVisitor : public AST::BaseVisitor { ASR::ttype_t* i64_type = ASRUtils::TYPE(ASR::make_Integer_t(al, x.base.base.loc, 8)); init_expr = ASRUtils::EXPR(ASR::make_IntegerConstant_t(al, x.base.base.loc, -1, i64_type)); } - visit_AnnAssignUtil(*ann_assign, var_name, init_expr, false, abi, true); + visit_AnnAssignUtil(*ann_assign, var_name, init_expr, false, abi, true, is_class_scope); ASR::symbol_t* var_sym = current_scope->resolve_symbol(var_name); ASR::call_arg_t c_arg; c_arg.loc = var_sym->base.loc; @@ -5425,20 +5430,22 @@ class BodyVisitor : public CommonVisitor { throw SemanticError("Variable: '" + std::string(n->m_id) + "' is not declared", x->base.loc); } - ASR::Variable_t* v = ASR::down_cast(s); - if (v->m_intent == ASR::intentType::In) { - std::string msg = "Hint: create a new local variable with a different name"; - if (ASRUtils::is_aggregate_type(v->m_type)) { - msg = "Use InOut[" + ASRUtils::type_to_str_python(v->m_type) + "] to allow assignment"; + if (ASR::is_a(*s)) { + ASR::Variable_t* v = ASR::down_cast(s); + if (v->m_intent == ASR::intentType::In) { + std::string msg = "Hint: create a new local variable with a different name"; + if (ASRUtils::is_aggregate_type(v->m_type)) { + msg = "Use InOut[" + ASRUtils::type_to_str_python(v->m_type) + "] to allow assignment"; + } + diag.add(diag::Diagnostic( + "Assignment to an input function parameter `" + + std::string(v->m_name) + "` is not allowed", + diag::Level::Error, diag::Stage::Semantic, { + diag::Label(msg, {x->base.loc}) + }) + ); + throw SemanticAbort(); } - diag.add(diag::Diagnostic( - "Assignment to an input function parameter `" - + std::string(v->m_name) + "` is not allowed", - diag::Level::Error, diag::Stage::Semantic, { - diag::Label(msg, {x->base.loc}) - }) - ); - throw SemanticAbort(); } return true; } else if (AST::is_a(*x)) { @@ -5583,15 +5590,17 @@ class BodyVisitor : public CommonVisitor { AST::Attribute_t *attr = AST::down_cast(x.m_targets[i]); if (AST::is_a(*attr->m_value)) { std::string name = AST::down_cast(attr->m_value)->m_id; - ASR::symbol_t *s = current_scope->get_symbol(name); + ASR::symbol_t *s = current_scope->resolve_symbol(name); if (!s) { throw SemanticError("Variable: '" + name + "' is not declared", x.base.base.loc); } - ASR::Variable_t *v = ASR::down_cast(s); - ASR::ttype_t *type = v->m_type; - if (ASRUtils::is_immutable(type)) { - throw SemanticError("readonly attribute", x.base.base.loc); + if (ASR::is_a(*s)) { + ASR::Variable_t *v = ASR::down_cast(s); + ASR::ttype_t *type = v->m_type; + if (ASRUtils::is_immutable(type)) { + throw SemanticError("readonly attribute", x.base.base.loc); + } } } }