Skip to content

Commit

Permalink
Add class members
Browse files Browse the repository at this point in the history
  • Loading branch information
xaerru committed Jan 31, 2025
1 parent 7eb2bea commit d6e350e
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 27 deletions.
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

0 comments on commit d6e350e

Please sign in to comment.