From 62d25bc7ce7d16a97a1a129888c9ef5fd2db8833 Mon Sep 17 00:00:00 2001 From: Ge Wang Date: Fri, 6 Dec 2024 19:37:46 -0800 Subject: [PATCH] support for this() as alternative ctor call --- src/core/chuck_absyn.cpp | 9 ++ src/core/chuck_absyn.h | 2 + src/core/chuck_compile.cpp | 4 +- src/core/chuck_emit.cpp | 11 ++ src/core/chuck_instr.cpp | 2 +- src/core/chuck_type.cpp | 173 ++++++++++++++---------- src/test/01-Basic/271-ctor-call-ctor.ck | 21 +++ src/test/01-Basic/272-this-call-ctor.ck | 23 ++++ 8 files changed, 167 insertions(+), 78 deletions(-) create mode 100644 src/test/01-Basic/271-ctor-call-ctor.ck create mode 100644 src/test/01-Basic/272-this-call-ctor.ck diff --git a/src/core/chuck_absyn.cpp b/src/core/chuck_absyn.cpp index 4223fe552..b59490bbd 100644 --- a/src/core/chuck_absyn.cpp +++ b/src/core/chuck_absyn.cpp @@ -1581,6 +1581,9 @@ void delete_exp( a_Exp e ) { // TODO: release reference type // TODO: release reference owner + // 1.5.4.4 (ge) actually -- the above may not be necessary if: + // 1) they are not ref-counted in the first place; e.g., see type_engine_check_exp() which assigned `type` + // AND 2) the AST is cleaned up before types and nspcs etc. // delete content in this exp delete_exp_contents( e ); @@ -1682,7 +1685,13 @@ void delete_exp_decl( a_Exp e ) void delete_exp_from_id( a_Exp_Primary e ) { // TODO: do we need to anything with the Symbol? + // 1.5.4.4 (ge) symbols are additive but also also unique per string thus growth bounded -- can keep around, in practice EM_log( CK_LOG_FINEST, "deleting exp (primary ID '%s') [%p]...", S_name(e->var), (void *)e ); + + // TODO: release reference func_alias #2024-ctor-this + // 1.5.4.4 (ge) actually -- the above may not be necessary if: + // 1) func_alisa not ref-counted in the first place; e.g., see xxx() which assigned `func_alias` + // AND 2) the AST is cleaned up before the func etc. } void delete_exp_from_str( a_Exp_Primary e ) diff --git a/src/core/chuck_absyn.h b/src/core/chuck_absyn.h index 9ae7c6e44..df181f0b3 100644 --- a/src/core/chuck_absyn.h +++ b/src/core/chuck_absyn.h @@ -368,6 +368,8 @@ struct a_Exp_Primary_ { ae_Exp_Primary_Type s_type; t_CKVALUE value; + // 1.5.4.4 (ge) added to alias a contructor to this() #2024-ctor-this + t_CKFUNC func_alias; union { diff --git a/src/core/chuck_compile.cpp b/src/core/chuck_compile.cpp index 434406f15..b2951209d 100644 --- a/src/core/chuck_compile.cpp +++ b/src/core/chuck_compile.cpp @@ -2510,8 +2510,6 @@ t_CKBOOL Chuck_Compiler::openFile( Chuck_CompileTarget * target, return FALSE; } - // open file - target->fd2parse = fopen( target->absolutePath.c_str(), "rb" ); // check for directory if( ck_isdir( target->absolutePath ) ) { @@ -2521,6 +2519,8 @@ t_CKBOOL Chuck_Compiler::openFile( Chuck_CompileTarget * target, return FALSE; } + // open file + target->fd2parse = fopen( target->absolutePath.c_str(), "rb" ); // if still unresolved if( !target->fd2parse ) { diff --git a/src/core/chuck_emit.cpp b/src/core/chuck_emit.cpp index 61c9554b9..48968aab2 100644 --- a/src/core/chuck_emit.cpp +++ b/src/core/chuck_emit.cpp @@ -4369,6 +4369,17 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, return FALSE; } + // get the function as exp | 1.5.4.4 (ge) added to support calling this() #2024-ctor-this + a_Exp exp_func = func_call->func; + // check if this() -- for calling another constructor from a constructor | 1.5.4.4 (ge) added + if( exp_func->s_type == ae_exp_primary && exp_func->primary.s_type == ae_primary_var && string(S_name(exp_func->primary.var)) == "this" ) + { + // NOTE should have already checked that we are within a class if `this` was used + assert( emit->env->class_def != NULL ); + // emit the contructor func | (don't need to dup last since we are not resolving from a dot_member + emit->append( new Chuck_Instr_Reg_Push_Imm( (t_CKUINT)exp_func->primary.func_alias ) ); + } + // line and pos t_CKUINT line = func_call->line; t_CKUINT where = func_call->where; diff --git a/src/core/chuck_instr.cpp b/src/core/chuck_instr.cpp index 85d51404a..a4a5de83d 100644 --- a/src/core/chuck_instr.cpp +++ b/src/core/chuck_instr.cpp @@ -5329,7 +5329,7 @@ const char * Chuck_Instr_Func_Call::params() const //----------------------------------------------------------------------------- // name: execute() -// desc: ... +// desc: general function call for in-language defined functions //----------------------------------------------------------------------------- void Chuck_Instr_Func_Call::execute( Chuck_VM * vm, Chuck_VM_Shred * shred ) { diff --git a/src/core/chuck_type.cpp b/src/core/chuck_type.cpp index 413d77e72..679f19b24 100644 --- a/src/core/chuck_type.cpp +++ b/src/core/chuck_type.cpp @@ -4814,99 +4814,122 @@ t_CKTYPE type_engine_check_exp_func_call( Chuck_Env * env, a_Exp exp_func, a_Exp return NULL; } - // make sure we have a function + // is exp_func's type a function? if( !isa( f, env->ckt_function ) ) { - EM_error2( exp_func->where, - "function call using a non-function value" ); - // check if f is of type Type | 1.5.2.5 (ge) added - if( equals( f, env->ckt_class ) ) + // check if this() -- for calling another constructor from a constructor | 1.5.4.4 (ge) added #2024-ctor-this + if( exp_func->s_type == ae_exp_primary && exp_func->primary.s_type == ae_primary_var && string(S_name(exp_func->primary.var)) == "this" ) + { + // NOTE should have already checked that we are within a class if `this` was used + assert( env->class_def != NULL ); + // make a temp AST ctor call struct + a_Ctor_Call_ ctor_call; + // populate the args field + ctor_call.args = args; + // match constructor by args + ctor_call.func = type_engine_check_ctor_call( env, env->class_def, &ctor_call, NULL, exp_func->where ); + // check for error + if( !ctor_call.func ) return NULL; + // set the function in question + theFunc = ctor_call.func; + // remember it as the func alias for `this` + exp_func->primary.func_alias = theFunc; + } + else // error case { - // provide hopefully helpful hint - EM_error2( 0, " |- (hint: creating an Object variable with a constructor?)" ); - EM_error2( 0, " |- (...if so, try using the form `%s VARNAME(...)` instead)", f->actual_type->name().c_str() ); + EM_error2( exp_func->where, + "function call using a non-function value" ); + // check if f is of type Type | 1.5.2.5 (ge) added + if( equals( f, env->ckt_class ) ) + { + // provide hopefully helpful hint + EM_error2( 0, " |- (hint: creating an Object variable with a constructor?)" ); + EM_error2( 0, " |- (...if so, try using the form `%s VARNAME(...)` instead)", f->actual_type->name().c_str() ); + } + return NULL; } - return NULL; } - - // copy the func - up = f->func_bridge; - - // check the arguments - if( args ) + else // exp_func's type is a function { - a = type_engine_check_exp( env, args ); - if( !a ) return NULL; - } - - // look for a match - t_CKBOOL hasError = FALSE; - theFunc = find_func_match( env, up, args, hasError, exp_func->where ); + // copy the func + up = f->func_bridge; - // no func - if( !theFunc ) - { - // hasError implies an error has already been printed - if( hasError ) return NULL; - - // if primary - if( exp_func->s_type == ae_exp_primary && exp_func->primary.s_type == ae_primary_var ) - { - EM_error2( exp_func->where, - "argument type(s) do not match...\n...for function '%s(...)'...", - S_name(exp_func->primary.var) ); - } - else if( exp_func->s_type == ae_exp_dot_member ) + // check the arguments + if( args ) { - EM_error2( exp_func->dot_member.where, - "argument type(s) do not match...\n...for function '%s(...)'...", - type_engine_print_exp_dot_member( env, &exp_func->dot_member ).c_str() ); + a = type_engine_check_exp( env, args ); + if( !a ) return NULL; } - else + + // look for a match + t_CKBOOL hasError = FALSE; + theFunc = find_func_match( env, up, args, hasError, exp_func->where ); + + // no func + if( !theFunc ) { - EM_error2( exp_func->where, - "argument type(s) do not match for function..." ); - } + // hasError implies an error has already been printed + if( hasError ) return NULL; - EM_error2( 0, - "...(please check the argument types)" ); + // if primary + if( exp_func->s_type == ae_exp_primary && exp_func->primary.s_type == ae_primary_var ) + { + EM_error2( exp_func->where, + "argument type(s) do not match...\n...for function '%s(...)'...", + S_name(exp_func->primary.var) ); + } + else if( exp_func->s_type == ae_exp_dot_member ) + { + EM_error2( exp_func->dot_member.where, + "argument type(s) do not match...\n...for function '%s(...)'...", + type_engine_print_exp_dot_member( env, &exp_func->dot_member ).c_str() ); + } + else + { + EM_error2( exp_func->where, + "argument type(s) do not match for function..." ); + } - return NULL; - } + EM_error2( 0, + "...(please check the argument types)" ); - // recheck the type with new name - if( exp_func->s_type == ae_exp_primary && exp_func->primary.s_type == ae_primary_var ) - { - // set the new name - // TODO: clear old - exp_func->primary.var = insert_symbol(theFunc->name.c_str()); - // make sure the type is still the name - if( *exp_func->type != *type_engine_check_exp( env, exp_func ) ) - { - // error - EM_error2( exp_func->where, - "(internal error) function type different on second check" ); return NULL; } - } - else if( exp_func->s_type == ae_exp_dot_member ) - { - // set the new name - // TODO: clear old - exp_func->dot_member.xid = insert_symbol(theFunc->name.c_str()); - /* - // TODO: figure if this is necessary - it type checks things twice! - // make sure the type is still the name - if( *exp_func->type != *type_engine_check_exp( env, exp_func ) ) + + // recheck the type with new name + if( exp_func->s_type == ae_exp_primary && exp_func->primary.s_type == ae_primary_var ) { - // error - EM_error2( exp_func->where, - "(internal error) function type different on second check" ); - return NULL; + // set the new name + // TODO: clear old + exp_func->primary.var = insert_symbol(theFunc->name.c_str()); + // make sure the type is still the name + if( *exp_func->type != *type_engine_check_exp( env, exp_func ) ) + { + // error + EM_error2( exp_func->where, + "(internal error) function type different on second check" ); + return NULL; + } } - */ + else if( exp_func->s_type == ae_exp_dot_member ) + { + // set the new name + // TODO: clear old + exp_func->dot_member.xid = insert_symbol(theFunc->name.c_str()); + /* + // TODO: figure if this is necessary - it type checks things twice! + // make sure the type is still the name + if( *exp_func->type != *type_engine_check_exp( env, exp_func ) ) + { + // error + EM_error2( exp_func->where, + "(internal error) function type different on second check" ); + return NULL; + } + */ + } + else assert( FALSE ); } - else assert( FALSE ); // copy ck_func out (return by reference) ck_func = theFunc; diff --git a/src/test/01-Basic/271-ctor-call-ctor.ck b/src/test/01-Basic/271-ctor-call-ctor.ck new file mode 100644 index 000000000..5b1570666 --- /dev/null +++ b/src/test/01-Basic/271-ctor-call-ctor.ck @@ -0,0 +1,21 @@ +class Foo +{ + int theX; + + // a constructor + fun Foo( int x ) { x => theX; } + + // another constructor + fun Foo() + { + // call a specific constructor + Foo( 5 ); + } +} + +// make a foo +Foo foo; + +// check +if( foo.theX == 5 ) <<< "success" >>>; + diff --git a/src/test/01-Basic/272-this-call-ctor.ck b/src/test/01-Basic/272-this-call-ctor.ck new file mode 100644 index 000000000..878193d3d --- /dev/null +++ b/src/test/01-Basic/272-this-call-ctor.ck @@ -0,0 +1,23 @@ +// test calling constructor using `this` + +class Foo +{ + int theX; + + // a constructor + fun Foo( int x ) { x => theX; } + + // another constructor + fun Foo() + { + // call a specific constructor + this( 5 ); + } +} + +// make a foo +Foo foo; + +// should print 5 +if( foo.theX == 5 ) <<< "success" >>>; +