From 6b8acda214c0b0b4c0a3dcc8f3202515c58c6bac Mon Sep 17 00:00:00 2001 From: Ge Wang Date: Sat, 30 Nov 2024 13:25:13 -0800 Subject: [PATCH] static variable instantiation #2024-static-init --- VERSIONS | 39 ++- examples/class/static-init.ck | 56 +++++ examples/import/import-test-3.ck | 15 ++ src/core/chuck_absyn.h | 5 +- src/core/chuck_emit.cpp | 223 +++++++++++++++--- src/core/chuck_instr.cpp | 6 +- src/core/chuck_type.cpp | 174 ++++++++++++-- src/core/chuck_type.h | 19 +- src/core/chuck_vm.cpp | 145 +++++++++++- src/core/chuck_vm.h | 40 +++- src/test/01-Basic/265-static-init.ck | 55 +++++ src/test/01-Basic/265-static-init.txt | 11 + src/test/01-Basic/266-static-init-import.ck | 14 ++ src/test/01-Basic/266-static-init-import.txt | 11 + src/test/01-Basic/267-static-init-more.ck | 32 +++ src/test/01-Basic/267-static-init-more.txt | 1 + .../06-Errors/error-public-class-func.txt | 2 +- .../06-Errors/error-static-init-local-f.ck | 13 + .../06-Errors/error-static-init-local-f.txt | 3 + .../06-Errors/error-static-init-local-v.ck | 13 + .../06-Errors/error-static-init-local-v.txt | 3 + .../06-Errors/error-static-init-member-f.ck | 13 + .../06-Errors/error-static-init-member-f.txt | 3 + .../06-Errors/error-static-init-member-v.ck | 14 ++ .../06-Errors/error-static-init-member-v.txt | 3 + 25 files changed, 846 insertions(+), 67 deletions(-) create mode 100644 examples/class/static-init.ck create mode 100644 examples/import/import-test-3.ck create mode 100644 src/test/01-Basic/265-static-init.ck create mode 100644 src/test/01-Basic/265-static-init.txt create mode 100644 src/test/01-Basic/266-static-init-import.ck create mode 100644 src/test/01-Basic/266-static-init-import.txt create mode 100644 src/test/01-Basic/267-static-init-more.ck create mode 100644 src/test/01-Basic/267-static-init-more.txt create mode 100644 src/test/06-Errors/error-static-init-local-f.ck create mode 100644 src/test/06-Errors/error-static-init-local-f.txt create mode 100644 src/test/06-Errors/error-static-init-local-v.ck create mode 100644 src/test/06-Errors/error-static-init-local-v.txt create mode 100644 src/test/06-Errors/error-static-init-member-f.ck create mode 100644 src/test/06-Errors/error-static-init-member-f.txt create mode 100644 src/test/06-Errors/error-static-init-member-v.ck create mode 100644 src/test/06-Errors/error-static-init-member-v.txt diff --git a/VERSIONS b/VERSIONS index f655ec983..8e6bb61ab 100644 --- a/VERSIONS +++ b/VERSIONS @@ -3,8 +3,45 @@ ChucK VERSIONS log ------------------ -1.5.4.3 +1.5.4.3 (December 2024) ======= +(added, at last) class static variable initialization for primitive + and Objects types. (Previously, static variables needed to be + instantiated and initialized separately.) Now they can, e.g., + ---------------------------------------- + class Foo + { + // declare a static int in class Foo + 5 => static int THE_NUM; + // declare a static SinOsc in class Foo + static SinOsc THE_UGEN(440); + } + // can be accessed as before + <<< Foo.THE_NUM, Foo.THE_UGEN.freq() >>>; + ---------------------------------------- + SEMANTICS for static variables + ---------------------------------------- + as before, static variable can only be DECLARED: + a) within a class definition + b) outside of any class function defintion + c) at the outer-most class scope (cannot be in nested { }) + ----------------------------------------- + COMPILE-TIME checks: + 1) a statement containing a static variable declaration + CANNOT access member data/functions + 2) otherwise, a statement can access both static and member data. + 3) a statement containing a static variable declaration + CANNOT access local (i.e., outside of class def) vars or funcs + even in non-public classes + ----------------------------------------- + RUNTIME checks: + 4) static initialization statements are run in immediate mode + meaning no time advances; this is enforced at runtime; any + time advances, including waiting on events, will result in + a runtime exception + ----------------------------------------- +(added) examples/class/static-init.ck + examples/import/import-test-3.ck (added) '/usr/lib/chuck' to @import system search paths on Linux and MacOS. .chug files in system paths will auto-load on start up (.ck files will not auto-load and needs to be @imported in order to be used); also diff --git a/examples/class/static-init.ck b/examples/class/static-init.ck new file mode 100644 index 000000000..e8f45ff09 --- /dev/null +++ b/examples/class/static-init.ck @@ -0,0 +1,56 @@ +// static variables & initialization example +// requires: chuck-1.5.4.3 or higher +// +//----------------------------------------- +// STATIC VARAIBLE INITIALIZATION SEMANTICS +//----------------------------------------- +// static variable can only be DECLARED: +// a) within a class definition +// b) outside of any class function defintion +// c) at the outer-most class scope (cannot be in nested { }) +//----------------------------------------- +// COMPILE-TIME checks: +// 1) a statement containing a static variable declaration +// CANNOT access member data/functions +// 2) otherwise, a statement can access both static and member data. +// 3) a statement containing a static variable declaration +// CANNOT access local (i.e., outside of class def) vars or funcs +// even in non-public classes +//----------------------------------------- +// RUNTIME checks: +// 4) static initialization statements are run in immediate mode +// meaning no time advances; this is enforced at runtime; any +// time advances, including waiting on events, will result in +// a runtime exception +//----------------------------------------- + +// a class +public class Foo +{ + // int + 1 => static int S_INT; + // float + 2 => static float S_FLOAT; + // dur + 3::second => static dur S_DUR; + // time + now + 4::second => static time S_TIME; + // vec3 + @(5,6,7) => static vec3 S_VEC3; + // array + [8,9,10,11] @=> static int S_INT_ARRAY[]; + // string + static string S_STRING("12"); + // ugen + static SinOsc S_SINOSC(440); +} + +// print static variables +<<< Foo.S_INT >>>; +<<< Foo.S_FLOAT >>>; +<<< Foo.S_DUR / second >>>; +<<< (Foo.S_TIME-now) / second >>>; +<<< Foo.S_VEC3 >>>; +for( auto i : Foo.S_INT_ARRAY ) <<< i >>>; +<<< Foo.S_STRING >>>; +<<< Foo.S_SINOSC.freq() >>>; diff --git a/examples/import/import-test-3.ck b/examples/import/import-test-3.ck new file mode 100644 index 000000000..0034825d8 --- /dev/null +++ b/examples/import/import-test-3.ck @@ -0,0 +1,15 @@ +// test basic static initialization across @import +// see examples/class/static-init.ck for more info + +// import +@import "../class/static-init.ck" + +// print static variables from imported class Foo +<<< Foo.S_INT >>>; +<<< Foo.S_FLOAT >>>; +<<< Foo.S_DUR / second >>>; +<<< (Foo.S_TIME-now) / second >>>; +<<< Foo.S_VEC3 >>>; +for( auto i : Foo.S_INT_ARRAY ) <<< i >>>; +<<< Foo.S_STRING >>>; +<<< Foo.S_SINOSC.freq() >>>; \ No newline at end of file diff --git a/src/core/chuck_absyn.h b/src/core/chuck_absyn.h index d20b68661..9ae7c6e44 100644 --- a/src/core/chuck_absyn.h +++ b/src/core/chuck_absyn.h @@ -467,8 +467,11 @@ struct a_Stmt_ ae_Stmt_Type s_type; // used to track control paths in non-void functions t_CKBOOL allControlPathsReturn; // 1.5.1.0 (ge) added - // number of obj refs that needs releasing after | 1.5.1.7 + // number of obj refs that needs releasing after | 1.5.1.7 (ge) added t_CKUINT numObjsToRelease; + // does stmt have a static variable declaration | 1.5.4.3 (ge) added for #2024-static-init + // (used to mark a statement as part of static initializer for a class) + t_CKBOOL hasStaticDecl; // mushed into one! union diff --git a/src/core/chuck_emit.cpp b/src/core/chuck_emit.cpp index 4fa4e074a..70819c03f 100644 --- a/src/core/chuck_emit.cpp +++ b/src/core/chuck_emit.cpp @@ -451,6 +451,35 @@ t_CKBOOL emit_engine_emit_stmt( Chuck_Emitter * emit, a_Stmt stmt, t_CKBOOL pop if( !stmt ) return TRUE; + // push stmt stack | 1.5.4.3 (ge) added to match the type system + // in order to do additional checks in the emitter #2024-static-init + emit->env->stmt_stack.push_back(stmt); + + // static initialization / initializer statements | 1.5.4.3 (ge) added #2024-static-init + // FYI semantics for static initializer (within class def but outside function def): + // 0) a single statement CANNOT read member data/call member functions AND init static data + // 1) otherwise, a single statement can access both static and member data + // these semantic rules should have been enforced in the type checker before this point + // so these are pre-conditions should we reach this point + // RUNTIME checks: + // 3) static initializer statements will be run in immediate mode, meaning no time advances; + // this is enforced at runtime; time advances will result in an exception + + // check if statement is a static initializer statement | 1.5.4.3 (ge) added + if( stmt->hasStaticDecl ) + { + // check pre-conditions + assert( emit->env->class_def != NULL ); + assert( emit->env->class_def->static_code_emit != NULL ); + + // get associated class + Chuck_Type * type = emit->env->class_def; + // push the current code sequender + emit->code_stack.push_back( emit->code ); + // set the static code sequencer as the current + emit->code = type->static_code_emit; + } + // return t_CKBOOL ret = TRUE; // next index @@ -681,6 +710,18 @@ t_CKBOOL emit_engine_emit_stmt( Chuck_Emitter * emit, a_Stmt stmt, t_CKBOOL pop emit->code->code[emit->next_index()-1]->append_codestr( codestr_close ); } + // if static initializer statement, restore + if( stmt->hasStaticDecl ) + { + // set the static code sequencer as the current + emit->code = emit->code_stack.back(); + // push the current code sequender + emit->code_stack.pop_back(); + } + + // pop stmt stack | 1.5.4.3 (ge) added as part of #2024-static-init + emit->env->stmt_stack.pop_back(); + return ret; } @@ -2080,11 +2121,11 @@ t_CKBOOL emit_engine_emit_exp_binary( Chuck_Emitter * emit, a_Exp_Binary binary // emit (doRef added 1.3.0.2) left = emit_engine_emit_exp( emit, binary->lhs, doRefLeft ); + if( !left ) return FALSE; right = emit_engine_emit_exp( emit, binary->rhs, doRefRight ); - + if( !right ) return FALSE; // check - if( !left || !right ) - return FALSE; + // if( !left || !right ) return FALSE; // emit the op if( !emit_engine_emit_op( emit, binary->op, binary->lhs, binary->rhs, binary ) ) @@ -4300,8 +4341,9 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, // emit func if( !emit_engine_emit_exp( emit, func_call->func ) ) { - EM_error2( func_call->where, - "(emit): internal error in evaluating function call..." ); + // 1.5.4.3 (ge) no longer necessarily an internal error #2024-static-init + // EM_error2( func_call->where, + // "(emit): internal error in evaluating function call..." ); return FALSE; } @@ -4575,8 +4617,7 @@ t_CKBOOL emit_engine_emit_exp_dot_member_special( Chuck_Emitter * emit, // the type of the base Chuck_Type * t_base = member->t_base; // is the base a class/namespace or a variable - t_CKBOOL base_static = type_engine_is_base_static( emit->env, member->t_base ); - // t_CKBOOL base_static = isa( member->ckt_base, emit->env->ckt_class ); + t_CKBOOL base_type_static = type_engine_is_base_type_static( emit->env, member->t_base ); // a function Chuck_Func * func = NULL; // a non-function value @@ -4585,7 +4626,7 @@ t_CKBOOL emit_engine_emit_exp_dot_member_special( Chuck_Emitter * emit, t_CKUINT offset = 0; // check error - if( base_static ) + if( base_type_static ) { // should not get here EM_error2( member->base->where, @@ -4668,8 +4709,9 @@ t_CKBOOL emit_engine_emit_exp_dot_member( Chuck_Emitter * emit, // whether to emit addr or value t_CKBOOL emit_addr = member->self->emit_var; // is the base a class/namespace or a variable | 1.5.0.0 (ge) modified to func call - t_CKBOOL base_static = type_engine_is_base_static( emit->env, member->t_base ); - // t_CKBOOL base_static = isa( member->ckt_base, emit->env->ckt_class ); + t_CKBOOL base_type_static = type_engine_is_base_type_static( emit->env, member->t_base ); + // check if the base exp is static-compatible, e.g., usable to initialize static var | 1.5.4.3 (ge) added as part of #2024-static-init + t_CKBOOL base_exp_static = type_engine_is_base_exp_static( emit->env, member ); // a function Chuck_Func * func = NULL; // a non-function value @@ -4698,13 +4740,39 @@ t_CKBOOL emit_engine_emit_exp_dot_member( Chuck_Emitter * emit, // actual type - if base is class name its type is actually 'class' // to get the actual type use actual_type - t_base = base_static ? member->t_base->actual_type : member->t_base; + t_base = base_type_static ? member->t_base->actual_type : member->t_base; // make sure that the base type is object assert( t_base->nspc != NULL ); + // get the value to test | 1.5.4.3 (ge) added as part of #2024-static-init + // NOTE: this type of check usually resides pre-emisssion, in the type-checker + // but in this case, the hasStaticDecl check happens in the type-checker + // and this verification needs to be come after that + Chuck_Value * v = type_engine_find_value( t_base, member->xid ); + if( v && (!base_type_static && !base_exp_static) + && emit->env->class_def && !emit->env->func + && (emit->env->stmt_stack.size() && emit->env->stmt_stack.back()->hasStaticDecl) ) + { + if( v->func_ref ) + { + if( v->is_member ) + { + EM_error2( member->where, + "cannot call non-static function '%s' to initialize a static variable", v->func_ref->signature(FALSE,FALSE).c_str() ); + return FALSE; + } + } + else if( !v->is_static ) + { + EM_error2( member->where, + "cannot access non-static variable '%s.%s' to initialize a static variable", v->owner_class->name().c_str(), v->name.c_str() ); + return FALSE; + } + } + // if base is static? - if( !base_static ) + if( !base_type_static ) { // if is a func if( isfunc( emit->env, member->self->type ) ) @@ -4718,8 +4786,8 @@ t_CKBOOL emit_engine_emit_exp_dot_member( Chuck_Emitter * emit, // is the func static? if( func->is_member ) { - // emit the base (TODO: return on error?) - emit_engine_emit_exp( emit, member->base ); + // emit the base + if( !emit_engine_emit_exp( emit, member->base ) ) { return FALSE; } // check if we are part of a function call vs. function as value // 1.5.4.3 (ge) added as part of #2024-func-call-update if( member->self->emit_as_funccall ) @@ -5748,7 +5816,7 @@ t_CKBOOL emit_engine_emit_func_def( Chuck_Emitter * emit, a_Func_Def func_def ) //----------------------------------------------------------------------------- // name: emit_engine_emit_class_def() -// desc: ... +// desc: emit bytecode for a class definition //----------------------------------------------------------------------------- t_CKBOOL emit_engine_emit_class_def( Chuck_Emitter * emit, a_Class_Def class_def ) { @@ -5802,6 +5870,19 @@ t_CKBOOL emit_engine_emit_class_def( Chuck_Emitter * emit, a_Class_Def class_def return FALSE; } + // static code initializer | 1.5.4.3 (ge) added as part of #2024-static-init + if( type->nspc->static_data_size > 0 ) + { + // create code container for emitting + type->static_code_emit = new Chuck_Code; + // name the code; copy the class code name + type->static_code_emit->name = emit->code->name + " (static initializer)"; + // need this? + type->static_code_emit->need_this = FALSE; + // keep track of filename; copy the class code filename + type->static_code_emit->filename = emit->code->filename; + } + // emit the body while( body && ret ) { @@ -5846,21 +5927,75 @@ t_CKBOOL emit_engine_emit_class_def( Chuck_Emitter * emit, a_Class_Def class_def CK_SAFE_REF_ASSIGN( type->nspc->pre_ctor, emit_to_code( emit->code, type->nspc->pre_ctor, emit->dump ) ); - // allocate static - type->nspc->class_data = new t_CKBYTE[type->nspc->class_data_size]; - // verify - if( !type->nspc->class_data ) - { - // we have a problem - CK_FPRINTF_STDERR( - "[chuck](emitter): OutOfMemory: while allocating static data '%s'\n", type->c_name() ); - // flag - ret = FALSE; - } - else - { - // zero it out - memset( type->nspc->class_data, 0, type->nspc->class_data_size ); + // ---------------------- + // static data and code + // check if we have static data? + if( type->nspc->static_data_size > 0 ) + { + // allocate static + type->nspc->static_data = new t_CKBYTE[type->nspc->static_data_size]; + // verify + if( !type->nspc->static_data ) + { + // we have a problem + CK_FPRINTF_STDERR( + "[chuck](emitter): OutOfMemory: while allocating static data '%s'\n", type->c_name() ); + // flag + ret = FALSE; + } + else + { + // zero it out + memset( type->nspc->static_data, 0, type->nspc->static_data_size ); + } + + // static initializer code | 1.5.4.3 (ge) added as part of #2024-static-init + //----------------------------------------- + // STATIC VARAIBLE INITIALIZATION SEMANTICS + //----------------------------------------- + // static variables must be DECLARED: + // a) within a class definition + // b) outside of any class function defintion + // c) at the outer-most class scope; can not be in nested { } + //----------------------------------------- + // COMPILE-TIME checks: + // 1) a statement containing a static variable declaration + // CANNOT access member data/functions + // 2) otherwise, a statement can access both static and member data + // 3) a statement containing a static variable declaration + // CANNOT access local (i.e., outside of class def) vars or funcs + // even in non-public classes + //----------------------------------------- + // RUNTIME checks: + // 4) static initialization statements are run in immediate mode + // meaning no time advances; this is enforced at runtime; any + // time advances, including waiting on events, will result in + // a runtime exception + //----------------------------------------- + // see if there is at least one static init instruction + if( type->static_code_emit->code.size() ) + { + // append EOC for the static initializer + type->static_code_emit->code.push_back( new Chuck_Instr_EOC ); + // static itor code => vm code + Chuck_VM_Code * static_code = emit_to_code( type->static_code_emit, NULL, emit->dump ); + + // make sure NULL + assert( type->nspc->static_invoker == NULL ); + // instantiate an invoker + type->nspc->static_invoker = new Chuck_VM_SInitInvoker; + // setup + type->nspc->static_invoker->setup( type, static_code, emit->env->vm() ); + // run the static initializer in immediate mode + type->nspc->static_invoker->invoke( emit->env->vm()->shreduler()->get_current_shred() ); + // clean up + CK_SAFE_DELETE( type->nspc->static_invoker ); + + // TODO: either defer the initializer to when we have a parent shred OR disallow context-level vars and func calls for static + // TODO: disallow return in class def body (member and static) + } + // clean up the code emit structure + CK_SAFE_DELETE( type->static_code_emit ); } } @@ -5912,8 +6047,9 @@ t_CKBOOL emit_engine_emit_spork( Chuck_Emitter * emit, a_Exp_Func_Call exp ) // emit func pointer on sporker shred if( !emit_engine_emit_exp( emit, exp->func ) ) { - EM_error2( exp->where, - "(emit): internal error in evaluating function call..." ); + // 1.5.4.3 (ge) no longer necessarily an internal error #2024-static-init + // EM_error2( exp->where, + // "(emit): internal error in evaluating function call..." ); return FALSE; } @@ -6095,9 +6231,10 @@ t_CKBOOL emit_engine_emit_symbol( Chuck_Emitter * emit, S_Symbol symbol, // emit it if( !emit_engine_emit_exp_dot_member( emit, &dot->dot_member ) ) { + // 1.5.4.3 (ge) no longer necessarily an internal error #2024-static-init // internal error - EM_error2( where, - "(emit): internal error: symbol transformation failed..." ); + // EM_error2( where, + // "(emit): internal error: symbol transformation failed..." ); return FALSE; } @@ -6112,6 +6249,24 @@ t_CKBOOL emit_engine_emit_symbol( Chuck_Emitter * emit, S_Symbol symbol, return TRUE; } + // 1.5.4.3 (ge) added this check #2024-static-init + // static variable declarations cannot access values that + if( v && v->is_context_global && !v->is_global + && emit->env->class_def && !emit->env->func && (emit->env->stmt_stack.size() && emit->env->stmt_stack.back()->hasStaticDecl) ) + { + if( v->func_ref ) + { + EM_error2( exp->where, + "cannot call local function '%s' to initialize a static variable", v->func_ref->signature(FALSE,FALSE).c_str() ); + } + else + { + EM_error2( exp->where, + "cannot access local variable '%s' to initialize a static variable", S_name( exp->var ) ); + } + return FALSE; + } + // var or value if( emit_var ) { diff --git a/src/core/chuck_instr.cpp b/src/core/chuck_instr.cpp index afca8ff51..93e416791 100644 --- a/src/core/chuck_instr.cpp +++ b/src/core/chuck_instr.cpp @@ -4658,7 +4658,7 @@ inline void instantiate_object( Chuck_VM * vm, Chuck_VM_Shred * shred, push_( reg_sp, (t_CKUINT)object ); // add reference? | 1.5.4.3 (ge) added this logic - // see Chuck_Instr_Instantiate_Object_Complete::execute() for explanation + // for explanation see: Chuck_Instr_Instantiate_Object_Complete::execute() if( addRef ) object->add_ref(); // call preconstructor @@ -7790,9 +7790,9 @@ void Chuck_Instr_Dot_Static_Data::execute( Chuck_VM * vm, Chuck_VM_Shred * shred // get the object pointer Chuck_Type * t_class = (Chuck_Type *)(*sp); // make sure - assert( (m_offset + m_size) <= t_class->nspc->class_data_size ); + assert( (m_offset + m_size) <= t_class->nspc->static_data_size ); // calculate the data pointer - data = (t_CKUINT)(t_class->nspc->class_data + m_offset); + data = (t_CKUINT)(t_class->nspc->static_data + m_offset); // emit addr or value if( m_emit_addr ) diff --git a/src/core/chuck_type.cpp b/src/core/chuck_type.cpp index fca1b9b3a..8170edc13 100644 --- a/src/core/chuck_type.cpp +++ b/src/core/chuck_type.cpp @@ -1168,9 +1168,60 @@ t_CKBOOL type_engine_check_stmt_list( Chuck_Env * env, a_Stmt_List list ) +//----------------------------------------------------------------------------- +// name: type_engine_verify_stmt_static(() +// desc: verify there are no semantic violations +//----------------------------------------------------------------------------- +t_CKBOOL type_engine_verify_stmt_static( Chuck_Env * env, a_Stmt stmt ) +{ + // check stmt + if( !stmt->hasStaticDecl ) return TRUE; + + // return value + t_CKBOOL ret = FALSE; + + // the type of stmt + switch( stmt->s_type ) + { + case ae_stmt_import: + case ae_stmt_break: + case ae_stmt_continue: + case ae_stmt_gotolabel: + // trivial accept + ret = TRUE; + break; + + case ae_stmt_if: + case ae_stmt_for: + case ae_stmt_foreach: + case ae_stmt_while: + case ae_stmt_until: + case ae_stmt_loop: + case ae_stmt_switch: + case ae_stmt_case: + case ae_stmt_return: + case ae_stmt_code: + default: + // shouldn't get here + EM_error2( stmt->where, + "(internal error) failed to detect illegal static var decl!", stmt->s_type ); + ret = FALSE; + break; + + case ae_stmt_exp: + // ret = ( type_engine_check_exp( env, stmt->stmt_exp ) != NULL ); + break; + } + + return ret; +} + + + + //----------------------------------------------------------------------------- // name: type_engine_check_stmt(() -// desc: ... +// desc: type-check a statement //----------------------------------------------------------------------------- t_CKBOOL type_engine_check_stmt( Chuck_Env * env, a_Stmt stmt ) { @@ -1287,6 +1338,14 @@ t_CKBOOL type_engine_check_stmt( Chuck_Env * env, a_Stmt stmt ) // pop stmt stack | 1.5.1.7 env->stmt_stack.pop_back(); + // check return value so far + // actually -- verification will be done during emission phase + // if( ret ) + // { + // if stmt has static, check for violations | 1.5.4.3 (ge) + // ret = type_engine_verify_stmt_static( env, stmt ); + // } + return ret; } @@ -3509,7 +3568,7 @@ t_CKTYPE type_engine_check_exp_primary( Chuck_Env * env, a_Exp_Primary exp ) if( v->func_ref ) { EM_error2( exp->where, - "cannot call local function '%s' from within a public class", S_name( exp->var ) ); + "cannot call local function '%s' from within a public class", v->func_ref->signature(FALSE,FALSE).c_str() ); } else { @@ -3529,7 +3588,7 @@ t_CKTYPE type_engine_check_exp_primary( Chuck_Env * env, a_Exp_Primary exp ) if( v->func_ref ) { EM_error2( exp->where, - "cannot call local function '%s' from within @destruct()", S_name( exp->var ) ); + "cannot call local function '%s' from within @destruct()", v->func_ref->signature(FALSE,FALSE).c_str() ); } else { @@ -4379,21 +4438,28 @@ t_CKTYPE type_engine_check_exp_decl_part2( Chuck_Env * env, a_Exp_Decl decl ) // flag value->is_static = TRUE; // offset - value->offset = env->class_def->nspc->class_data_size; + value->offset = env->class_def->nspc->static_data_size; // move the size - env->class_def->nspc->class_data_size += type->size; + env->class_def->nspc->static_data_size += type->size; - // if this is an object - if( is_obj && !is_ref ) + // if we are located inside a statement + if( env->stmt_stack.size() ) { - // for now - no good for static, since we need separate - // initialization which we don't have - EM_error2( var_decl->where, - "cannot declare static non-primitive objects (yet)..." ); - EM_error2( 0, - "...(hint: declare as reference (@) & initialize outside class for now)" ); - return NULL; + // mark containing statement as having static decl | 1.5.4.3 (ge) added as part of #2024-static-init + env->stmt_stack.back()->hasStaticDecl = TRUE; } + + // // if this is an object + // if( is_obj && !is_ref ) + // { + // // for now - no good for static, since we need separate + // // initialization which we don't have + // EM_error2( var_decl->where, + // "cannot declare static non-primitive objects (yet)..." ); + // EM_error2( 0, + // "...(hint: declare as reference (@) & initialize outside class for now)" ); + // return NULL; + // } } else // local variable { @@ -4457,7 +4523,7 @@ string type_engine_print_exp_dot_member( Chuck_Env * env, a_Exp_Dot_Member membe if( !member->t_base ) return "[error]"; // is the base a class/namespace or a variable | modified 1.5.0.0 (ge) - base_static = type_engine_is_base_static( env, member->t_base ); + base_static = type_engine_is_base_type_static( env, member->t_base ); // base_static = isa( member->t_base, env->ckt_class ); // actual type @@ -5005,7 +5071,7 @@ t_CKTYPE type_engine_check_exp_dot_member_special( Chuck_Env * env, a_Exp_Dot_Me t_CKBOOL base_static = FALSE; // is the base a class/namespace or a variable | 1.5.0.0 (ge) modified to call - base_static = type_engine_is_base_static( env, member->t_base ); + base_static = type_engine_is_base_type_static( env, member->t_base ); // base_static = isa( member->t_base, env->ckt_class ); // actual type the_base = base_static ? member->t_base->actual_type : member->t_base; @@ -5071,7 +5137,7 @@ t_CKTYPE type_engine_check_exp_dot_member( Chuck_Env * env, a_Exp_Dot_Member mem } // is the base a class/namespace or a variable - base_static = type_engine_is_base_static( env, member->t_base ); + base_static = type_engine_is_base_type_static( env, member->t_base ); // base_static = isa( member->t_base, env->ckt_class ) // actual type the_base = base_static ? member->t_base->actual_type : member->t_base; @@ -5634,8 +5700,12 @@ Chuck_Namespace::Chuck_Namespace() pre_dtor = NULL; parent = NULL; offset = 0; - class_data = NULL; - class_data_size = 0; + + // static-specific + static_data = NULL; + static_data_size = 0; + static_is_init = FALSE; + static_invoker = NULL; } @@ -5652,6 +5722,9 @@ Chuck_Namespace::~Chuck_Namespace() CK_SAFE_RELEASE( pre_dtor ); // TODO: release ref // CK_SAFE_RELEASE( this->parent ); + + // delete invoker + CK_SAFE_DELETE( static_invoker ); } @@ -6202,7 +6275,7 @@ Chuck_Value * type_engine_check_const( Chuck_Env * env, a_Exp exp ) // catch things like `1 => Math.PI` a_Exp_Dot_Member member = &exp->dot_member; // is the base a class/namespace or a variable | 1.5.0.0 (ge) modified to call - t_CKBOOL base_static = type_engine_is_base_static( env, member->t_base ); + t_CKBOOL base_static = type_engine_is_base_type_static( env, member->t_base ); // actual type Chuck_Type * the_base = base_static ? member->t_base->actual_type : member->t_base; @@ -9051,12 +9124,12 @@ t_CKBOOL type_engine_add_class_from_dl( Chuck_Env * env, Chuck_DL_Class * c ) //----------------------------------------------------------------------------- -// name: type_engine_is_base_static() | 1.5.0.0 (ge) added +// name: type_engine_is_base_type_static() | 1.5.0.0 (ge) added // desc: check for static func/member access using class; e.g., SinOsc.help() // this function was created after adding the complexity of t_class being // made available in the language (as the Type type) //----------------------------------------------------------------------------- -t_CKBOOL type_engine_is_base_static( Chuck_Env * env, Chuck_Type * baseType ) +t_CKBOOL type_engine_is_base_type_static( Chuck_Env * env, Chuck_Type * baseType ) { // check if( baseType == NULL ) return FALSE; @@ -9067,6 +9140,62 @@ t_CKBOOL type_engine_is_base_static( Chuck_Env * env, Chuck_Type * baseType ) +//----------------------------------------------------------------------------- +// name: type_engine_is_base_exp_static() | 1.5.4.3 (ge) added +// desc: check if an dotmember base is static compatible; used in static var initialization +// verification; #2024-static-init +//----------------------------------------------------------------------------- +t_CKBOOL type_engine_is_base_exp_static( Chuck_Env * env, a_Exp_Dot_Member exp ) +{ + // check + switch( exp->base->s_type ) + { + case ae_exp_primary: + // if we have a type-checked value... + if( exp->base->primary.value ) + { + // check base type + if( type_engine_is_base_type_static( env, exp->base->primary.value->type ) ) { return TRUE; } + // return if value static + return exp->base->primary.value->is_static; + } + // or value could be NULL, e.g., in just-in-time implicit constructs for "this" + else + { + return FALSE; + } + break; + + case ae_exp_dot_member: + { + // recursive check + if( type_engine_is_base_exp_static( env, &exp->base->dot_member ) ) return TRUE; + + // next check after the dot; get value to test + Chuck_Value * v = type_engine_find_value( exp->base->dot_member.t_base, exp->base->dot_member.xid ); + // check it (shouldn't be NULL) + if( !v ) return FALSE; + + // func or var? + if( v->func_ref ) // func + return !(v->func_ref->is_member); + else // var + return v->is_static; + break; + } + + default: + // everything else + return FALSE; + break; + } + + return FALSE; +} + + + + //----------------------------------------------------------------------------- // name: type_engine_binary_is_func_call() | 1.5.4.3 (ge) // desc: check whether a binary expression is a function call @@ -9882,6 +10011,7 @@ Chuck_Type::Chuck_Type( Chuck_Env * env, te_Type _id, const std::string & _n, dtor_the = NULL; dtor_invoker = NULL; allocator = NULL; + static_code_emit = NULL; // default originHint = ckte_origin_UNKNOWN; diff --git a/src/core/chuck_type.h b/src/core/chuck_type.h index 41550435e..e027a0e96 100644 --- a/src/core/chuck_type.h +++ b/src/core/chuck_type.h @@ -287,10 +287,12 @@ struct Chuck_Type; struct Chuck_Value; struct Chuck_Func; struct Chuck_Multi; +struct Chuck_Code; // 1.5.4.3 struct Chuck_VM; struct Chuck_VM_Code; struct Chuck_VM_MFunInvoker; struct Chuck_VM_DtorInvoker; +struct Chuck_VM_SInitInvoker; struct Chuck_DLL; // operator loading structs | 1.5.1.5 struct Chuck_Op_Registry; @@ -314,9 +316,13 @@ struct Chuck_Namespace : public Chuck_VM_Object // virtual table Chuck_VTable obj_v_table; // static data segment - t_CKBYTE * class_data; + t_CKBYTE * static_data; // static data segment size - t_CKUINT class_data_size; + t_CKUINT static_data_size; + // has static init been run? + t_CKBOOL static_is_init; + // static initializer (to be run once per class) + Chuck_VM_SInitInvoker * static_invoker; // name std::string name; @@ -1041,9 +1047,15 @@ struct Chuck_Type : public Chuck_Object // offsets of mvars that are Objects std::vector obj_mvars_offsets; +public: + // current static init code for emitter | 1.5.4.3 (ge) added #2024-static-init + Chuck_Code * static_code_emit; + +public: // (within-context, e.g., a ck file) dependency tracking | 1.5.0.8 Chuck_Value_Dependency_Graph depends; +public: // documentation std::string doc; // example files @@ -1413,7 +1425,8 @@ t_CKBOOL type_engine_check_primitive( Chuck_Env * env, Chuck_Type * type ); t_CKBOOL type_engine_check_const( Chuck_Env * env, a_Exp e, int pos ); // TODO t_CKBOOL type_engine_compat_func( a_Func_Def lhs, a_Func_Def rhs, int pos, std::string & err, t_CKBOOL print = TRUE ); t_CKBOOL type_engine_get_deprecate( Chuck_Env * env, const std::string & from, std::string & to ); -t_CKBOOL type_engine_is_base_static( Chuck_Env * env, Chuck_Type * baseType ); // 1.5.0.0 (ge) added +t_CKBOOL type_engine_is_base_type_static( Chuck_Env * env, Chuck_Type * baseType ); // 1.5.0.0 (ge) added +t_CKBOOL type_engine_is_base_exp_static( Chuck_Env * env, a_Exp_Dot_Member exp ); // 1.5.4.3 (ge) added #2024-static-init t_CKBOOL type_engine_binary_is_func_call( Chuck_Env * env, ae_Operator op, a_Exp lhs, a_Exp rhs ); // 1.5.4.3 (ge) added Chuck_Type * type_engine_find_common_anc( Chuck_Type * lhs, Chuck_Type * rhs ); Chuck_Type * type_engine_find_type( Chuck_Env * env, a_Id_List path ); diff --git a/src/core/chuck_vm.cpp b/src/core/chuck_vm.cpp index 875eb8e96..b15d7e37e 100644 --- a/src/core/chuck_vm.cpp +++ b/src/core/chuck_vm.cpp @@ -3643,6 +3643,11 @@ t_CKBOOL Chuck_VM_MFunInvoker::setup( Chuck_Func * func, t_CKUINT func_vt_offset // create dedicated shred invoker_shred = new Chuck_VM_Shred; + // add reference (although we will hard-delete this shred in cleanup() + // this is needed in case references are added/released along the way, + // e.g., as part of UGens created on this shred, which would add_ref + // as part of origin shred, and would release ref in shred.detach_ugens() | 1.5.4.3 (ge) added + invoker_shred->add_ref(); // set the VM ref (needed by initialize) invoker_shred->vm_ref = vm; // initialize with code + allocate stacks @@ -3822,8 +3827,9 @@ Chuck_DL_Return Chuck_VM_MFunInvoker::invoke( Chuck_Object * obj, const vectoradd_ref(); // set the VM ref (needed by initialize) invoker_shred->vm_ref = vm; // initialize with code + allocate stacks @@ -3982,6 +3993,138 @@ void Chuck_VM_DtorInvoker::cleanup() +//----------------------------------------------------------------------------- +// name: Chuck_VM_SInitInvoker() +// desc: constructor +//----------------------------------------------------------------------------- +Chuck_VM_SInitInvoker::Chuck_VM_SInitInvoker() +{ + // zero + invoker_shred = NULL; + the_code = NULL; + the_class = NULL; +} + + + + +//----------------------------------------------------------------------------- +// name: ~Chuck_VM_SInitInvoker() +// desc: destructor +//----------------------------------------------------------------------------- +Chuck_VM_SInitInvoker::~Chuck_VM_SInitInvoker() +{ + cleanup(); +} + + + + +//----------------------------------------------------------------------------- +// name: setup() +// desc: setup the invoker for use; static_itor should end with EOC +//----------------------------------------------------------------------------- +t_CKBOOL Chuck_VM_SInitInvoker::setup( Chuck_Type * type, + Chuck_VM_Code * static_code, + Chuck_VM * vm ) +{ + // clean up first + if( invoker_shred ) cleanup(); + + // remember + CK_SAFE_REF_ASSIGN( the_code, static_code ); + CK_SAFE_REF_ASSIGN( the_class, type ); + + // create dedicated shred + invoker_shred = new Chuck_VM_Shred; + // add reference (although we will hard-delete this shred in cleanup() + // this is needed in case references are added/released along the way, + // e.g., as part of UGens created on this shred, which would add_ref + // as part of origin shred, and would release ref in shred.detach_ugens() + invoker_shred->add_ref(); + // set the VM ref (needed by initialize) + invoker_shred->vm_ref = vm; + // initialize with code + allocate stacks + invoker_shred->initialize( static_code ); + // set name + invoker_shred->name = static_code->name; + // enter immediate mode (will throw runtime exception on any time/event ops) + invoker_shred->setImmediateMode( TRUE ); + + // done + return TRUE; +} + + + + +//----------------------------------------------------------------------------- +// name: invoke() +// desc: invoke the dtor +//----------------------------------------------------------------------------- +void Chuck_VM_SInitInvoker::invoke( Chuck_VM_Shred * parent_shred ) +{ + // no shred? + if( !invoker_shred ) return; + // verify + assert( the_class != NULL ); + + // log + EM_log( CK_LOG_DEBUG, "initializing static data for '%s'", the_class->name().c_str() ); + + // reset shred: program counter + invoker_shred->pc = 0; + // next pc + invoker_shred->next_pc = 1; + // set parent + invoker_shred->parent = parent_shred; + + // set parent base_ref; in case mfun is part of a non-public class + // that can access file-global variables outside the class definition + if( invoker_shred->parent ) + { invoker_shred->base_ref = invoker_shred->parent->base_ref; } + else + { invoker_shred->base_ref = invoker_shred->mem; } + + // shred in dump (all done) + invoker_shred->is_dumped = FALSE; + // shred done + invoker_shred->is_done = FALSE; + // shred running + invoker_shred->is_running = FALSE; + // shred abort + invoker_shred->is_abort = FALSE; + // set the instr + invoker_shred->instr = invoker_shred->code->instr; + // zero out the id (shred is in immediate mode and cannot be shreduled) + invoker_shred->xid = 0; + // inherit now from vm + invoker_shred->now = invoker_shred->vm_ref->now(); + // run shred on VM + invoker_shred->run( invoker_shred->vm_ref ); +} + + + + +//----------------------------------------------------------------------------- +// name: cleanup() +// desc: clean up +//----------------------------------------------------------------------------- +void Chuck_VM_SInitInvoker::cleanup() +{ + // release type reference + CK_SAFE_RELEASE( the_class ); + // release code reference + CK_SAFE_RELEASE( the_code ); + + // DELETE invoker shred + CK_SAFE_DELETE( invoker_shred ); +} + + + + // begin CK_VM_DEBUG implementation //----------------------------------------------------------------------------- #if CK_VM_DEBUG_ENABLE diff --git a/src/core/chuck_vm.h b/src/core/chuck_vm.h index e170e95e5..b90782a43 100644 --- a/src/core/chuck_vm.h +++ b/src/core/chuck_vm.h @@ -792,6 +792,12 @@ struct Chuck_VM : public Chuck_Object // TODO: vector? (added 1.3.0.0 to fix uber-crash) std::list m_event_buffers; +protected: + // 1.5.4.3 (ge) added static initializer run queue + // types that need static init are added to this by the emitter + // this queue is checked and run in immediate mode (and on a dedicated shred) + std::list m_static_init_queue; + protected: // 1.4.1.0 (jack): manager for global variables Chuck_Globals_Manager * m_globals_manager; @@ -965,7 +971,7 @@ struct Chuck_VM_DtorInvoker public: // set up the invoker; needed before invoke() t_CKBOOL setup( Chuck_Func * func, Chuck_VM * vm ); - // invoke the member function + // invoke the dtor void invoke( Chuck_Object * obj, Chuck_VM_Shred * parent_shred = NULL ); // clean up void cleanup(); @@ -980,6 +986,38 @@ struct Chuck_VM_DtorInvoker +//----------------------------------------------------------------------------- +// name: struct Chuck_VM_SInitInvoker | 1.5.4.3 (ge) added #2024-static-init +// desc: aparatus for calling chuck-defined class static initialization +//----------------------------------------------------------------------------- +struct Chuck_VM_SInitInvoker +{ +public: + // constructor + Chuck_VM_SInitInvoker(); + // destructor + ~Chuck_VM_SInitInvoker(); + +public: + // set up the invoker; needed before invoke() + t_CKBOOL setup( Chuck_Type * type, Chuck_VM_Code * static_init_code, Chuck_VM * vm ); + // invoke the static function + void invoke( Chuck_VM_Shred * parent_shred = NULL ); + // clean up + void cleanup(); + +public: + // dedicated shred to call the static on, since this could be running "outside of chuck time" + Chuck_VM_Shred * invoker_shred; + // the code to run + Chuck_VM_Code * the_code; + // the class + Chuck_Type * the_class; +}; + + + + //----------------------------------------------------------------------------- // VM debug macros | 1.5.0.5 (ge) added // these are governed by the presence of __CHUCK_DEBUG__ (e.g., from makefile) diff --git a/src/test/01-Basic/265-static-init.ck b/src/test/01-Basic/265-static-init.ck new file mode 100644 index 000000000..5c2fa9642 --- /dev/null +++ b/src/test/01-Basic/265-static-init.ck @@ -0,0 +1,55 @@ +// test basic static initialization #2024-static-init + +//----------------------------------------- +// STATIC VARAIBLE INITIALIZATION SEMANTICS +//----------------------------------------- +// static variables can only be delcared: +// a) within a class definition +// b) outside of any class function defintion +// c) at the outer-most class scope (cannot be in nested { }) +//----------------------------------------- +// COMPILE-TIME checks: +// 1) a statement containing a static variable declaration +// CANNOT access member data/functions +// 2) otherwise, a statement can access both static and member data. +// 3) a statement containing a static variable declaration +// CANNOT access local (i.e., outside of class def) vars or funcs +// even in non-public classes +//----------------------------------------- +// RUNTIME checks: +// 4) static initialization statements are run in immediate mode +// meaning no time advances; this is enforced at runtime; any +// time advances, including waiting on events, will result in +// a runtime exception +//----------------------------------------- + +// a class +public class Foo +{ + // int + 1 => static int S_INT; + // float + 2 => static float S_FLOAT; + // dur + 3::second => static dur S_DUR; + // time + now + 4::second => static time S_TIME; + // vec3 + @(5,6,7) => static vec3 S_VEC3; + // array + [8,9,10,11] @=> static int S_INT_ARRAY[]; + // string + static string S_STRING("12"); + // ugen + static SinOsc S_SINOSC(440); +} + +// print static variables +<<< Foo.S_INT >>>; +<<< Foo.S_FLOAT >>>; +<<< Foo.S_DUR / second >>>; +<<< (Foo.S_TIME-now) / second >>>; +<<< Foo.S_VEC3 >>>; +for( auto i : Foo.S_INT_ARRAY ) <<< i >>>; +<<< Foo.S_STRING >>>; +<<< Foo.S_SINOSC.freq() >>>; diff --git a/src/test/01-Basic/265-static-init.txt b/src/test/01-Basic/265-static-init.txt new file mode 100644 index 000000000..90212f5e6 --- /dev/null +++ b/src/test/01-Basic/265-static-init.txt @@ -0,0 +1,11 @@ +1 :(int) +2.000000 :(float) +3.000000 :(float) +4.000000 :(float) +@(5.0000,6.0000,7.0000) :(vec3) +8 :(int) +9 :(int) +10 :(int) +11 :(int) +"12" :(string) +440.000000 :(float) diff --git a/src/test/01-Basic/266-static-init-import.ck b/src/test/01-Basic/266-static-init-import.ck new file mode 100644 index 000000000..d68b525b6 --- /dev/null +++ b/src/test/01-Basic/266-static-init-import.ck @@ -0,0 +1,14 @@ +// test basic static initialization across @import #2024-static-init + +// import +@import "265-static-init.ck" + +// print static variables +<<< Foo.S_INT >>>; +<<< Foo.S_FLOAT >>>; +<<< Foo.S_DUR / second >>>; +<<< (Foo.S_TIME-now) / second >>>; +<<< Foo.S_VEC3 >>>; +for( auto i : Foo.S_INT_ARRAY ) <<< i >>>; +<<< Foo.S_STRING >>>; +<<< Foo.S_SINOSC.freq() >>>; \ No newline at end of file diff --git a/src/test/01-Basic/266-static-init-import.txt b/src/test/01-Basic/266-static-init-import.txt new file mode 100644 index 000000000..90212f5e6 --- /dev/null +++ b/src/test/01-Basic/266-static-init-import.txt @@ -0,0 +1,11 @@ +1 :(int) +2.000000 :(float) +3.000000 :(float) +4.000000 :(float) +@(5.0000,6.0000,7.0000) :(vec3) +8 :(int) +9 :(int) +10 :(int) +11 :(int) +"12" :(string) +440.000000 :(float) diff --git a/src/test/01-Basic/267-static-init-more.ck b/src/test/01-Basic/267-static-init-more.ck new file mode 100644 index 000000000..4e2335eb6 --- /dev/null +++ b/src/test/01-Basic/267-static-init-more.ck @@ -0,0 +1,32 @@ +// static variable initialization +// some extended tests for static variable initialization +// #2024-static-init + +// a class +class Bar +{ + // a non-static function + fun int bar() { return 1; } +} + +// another class +public class Foo +{ + // a static function + fun static int foo() { return 2; } + + // static Bar object + static Bar myBar; + + // OK: using static object myBar to initialize a static var + myBar.bar() => static int myFoo1; + // OK: explicitly using Foo.myBar.bar() to initialize a static var + Foo.myBar.bar() => static int myFoo2; + // OK: although uses `this` AND bar() is not static, myBar is static + this.myBar.bar() => static int myFoo3; + // OK: although uses `this`, the function foo() is static + this.foo() => static int myFoo4; +} + +// should print 1 1 1 2 +<<< Foo.myFoo1, Foo.myFoo2, Foo.myFoo3, Foo.myFoo4 >>>; diff --git a/src/test/01-Basic/267-static-init-more.txt b/src/test/01-Basic/267-static-init-more.txt new file mode 100644 index 000000000..f6694183a --- /dev/null +++ b/src/test/01-Basic/267-static-init-more.txt @@ -0,0 +1 @@ +1 1 1 2 diff --git a/src/test/06-Errors/error-public-class-func.txt b/src/test/06-Errors/error-public-class-func.txt index af791a1b2..d6acea3e7 100644 --- a/src/test/06-Errors/error-public-class-func.txt +++ b/src/test/06-Errors/error-public-class-func.txt @@ -1,3 +1,3 @@ -error-public-class-func.ck:15:5: error: cannot call local function 'foo' from within a public class +error-public-class-func.ck:15:5: error: cannot call local function 'foo()' from within a public class [15] foo(); ^ diff --git a/src/test/06-Errors/error-static-init-local-f.ck b/src/test/06-Errors/error-static-init-local-f.ck new file mode 100644 index 000000000..b589fdbde --- /dev/null +++ b/src/test/06-Errors/error-static-init-local-f.ck @@ -0,0 +1,13 @@ +// error test: calling local (non-class) fun in static initialization +// #2024-static-init + +// local function +fun int foo() { return 5; } + +// non-public class (calling local functions allowed, except +// for static initialization) +class Foo +{ + // ERROR calling local fun in static initialization statement + foo() => static int THE_NUM; +} diff --git a/src/test/06-Errors/error-static-init-local-f.txt b/src/test/06-Errors/error-static-init-local-f.txt new file mode 100644 index 000000000..d4d4deaff --- /dev/null +++ b/src/test/06-Errors/error-static-init-local-f.txt @@ -0,0 +1,3 @@ +error-static-init-local-f.ck:12:5: error: cannot call local function 'foo()' to initialize a static variable +[12] foo() => static int THE_NUM; + ^ diff --git a/src/test/06-Errors/error-static-init-local-v.ck b/src/test/06-Errors/error-static-init-local-v.ck new file mode 100644 index 000000000..88ebab786 --- /dev/null +++ b/src/test/06-Errors/error-static-init-local-v.ck @@ -0,0 +1,13 @@ +// error test: using local (non-class) var in static initialization +// #2024-static-init + +// local var +5 => int N; + +// non-public class (accessing local variables allowed, except +// for static initialization) +class Foo +{ + // ERROR using local variable to initialize static variable + N => static int THE_NUM; +} diff --git a/src/test/06-Errors/error-static-init-local-v.txt b/src/test/06-Errors/error-static-init-local-v.txt new file mode 100644 index 000000000..dc1fadf9f --- /dev/null +++ b/src/test/06-Errors/error-static-init-local-v.txt @@ -0,0 +1,3 @@ +error-static-init-local-v.ck:12:5: error: cannot access local variable 'N' to initialize a static variable +[12] N => static int THE_NUM; + ^ diff --git a/src/test/06-Errors/error-static-init-member-f.ck b/src/test/06-Errors/error-static-init-member-f.ck new file mode 100644 index 000000000..4be4bdc05 --- /dev/null +++ b/src/test/06-Errors/error-static-init-member-f.ck @@ -0,0 +1,13 @@ +// error test: calling member function in static initialization +// #2024-static-init + +// a class +class Foo +{ + // ERROR calling member function to initialize static var + foo() => static int THE_NUM; + + // member function + fun int foo() { return 5; } +} + diff --git a/src/test/06-Errors/error-static-init-member-f.txt b/src/test/06-Errors/error-static-init-member-f.txt new file mode 100644 index 000000000..4ce7efd8b --- /dev/null +++ b/src/test/06-Errors/error-static-init-member-f.txt @@ -0,0 +1,3 @@ +error-static-init-member-f.ck:8:5: error: cannot call non-static function 'Foo.foo()' to initialize a static variable +[8] foo() => static int THE_NUM; + ^ diff --git a/src/test/06-Errors/error-static-init-member-v.ck b/src/test/06-Errors/error-static-init-member-v.ck new file mode 100644 index 000000000..f950e9803 --- /dev/null +++ b/src/test/06-Errors/error-static-init-member-v.ck @@ -0,0 +1,14 @@ +// error test: using member var in static initialization +// #2024-static-init + +// a class +class Foo +{ + // member variable + 1 => int n; + 2 => int m; + + // ERROR using member variable to initialize + n + m => static int THE_NUM; +} + diff --git a/src/test/06-Errors/error-static-init-member-v.txt b/src/test/06-Errors/error-static-init-member-v.txt new file mode 100644 index 000000000..a89a4d63e --- /dev/null +++ b/src/test/06-Errors/error-static-init-member-v.txt @@ -0,0 +1,3 @@ +error-static-init-member-v.ck:12:5: error: cannot access non-static variable 'Foo.n' to initialize a static variable +[12] n + m => static int THE_NUM; + ^