Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for class members #2821

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions integration_tests/class_07.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 1 addition & 1 deletion src/libasr/ASR.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/libasr/asr_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/libasr/asr_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@ class VerifyVisitor : public BaseWalkVisitor<VerifyVisitor>
s = ASRUtils::symbol_get_past_external(x.m_v);
}
require(is_a<Variable_t>(*s) || is_a<Function_t>(*s)
|| is_a<ASR::EnumType_t>(*s) || is_a<ASR::ExternalSymbol_t>(*s),
|| is_a<ASR::EnumType_t>(*s) || is_a<ASR::ExternalSymbol_t>(*s) || is_a<ASR::Struct_t>(*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),
Expand Down
35 changes: 34 additions & 1 deletion src/libasr/codegen/asr_to_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2732,6 +2732,11 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
}
ASR::Variable_t* member = down_cast<ASR::Variable_t>(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() ) {
Expand Down Expand Up @@ -2763,6 +2768,24 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
}
}


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<ASR::Variable_t>(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 &&
Expand All @@ -2782,7 +2805,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
// (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;
Expand Down Expand Up @@ -3127,6 +3150,15 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
instantiate_function(*v);
}
}
for (size_t i = 0; i < x.n_members; i++) {
ASR::Variable_t *v = down_cast<ASR::Variable_t>(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;
}

Expand Down Expand Up @@ -4956,6 +4988,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
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 ) {
Expand Down
57 changes: 33 additions & 24 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2669,9 +2669,9 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {

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;
Expand Down Expand Up @@ -2891,7 +2891,7 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
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 ) {
Expand All @@ -2917,8 +2917,13 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
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);
Expand Down Expand Up @@ -3082,7 +3087,7 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
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;
Expand Down Expand Up @@ -5425,20 +5430,22 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
throw SemanticError("Variable: '" + std::string(n->m_id) + "' is not declared",
x->base.loc);
}
ASR::Variable_t* v = ASR::down_cast<ASR::Variable_t>(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<ASR::Variable_t>(*s)) {
ASR::Variable_t* v = ASR::down_cast<ASR::Variable_t>(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<AST::Subscript_t>(*x)) {
Expand Down Expand Up @@ -5583,15 +5590,17 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
AST::Attribute_t *attr = AST::down_cast<AST::Attribute_t>(x.m_targets[i]);
if (AST::is_a<AST::Name_t>(*attr->m_value)) {
std::string name = AST::down_cast<AST::Name_t>(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<ASR::Variable_t>(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<ASR::Variable_t>(*s)) {
ASR::Variable_t *v = ASR::down_cast<ASR::Variable_t>(s);
ASR::ttype_t *type = v->m_type;
if (ASRUtils::is_immutable(type)) {
throw SemanticError("readonly attribute", x.base.base.loc);
}
}
}
}
Expand Down
Loading