From 82913240ac908750e121f8a7bd9537eaaace1798 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Thu, 29 Apr 2021 23:19:45 -0400 Subject: [PATCH 01/14] fbc: g++ ABI - prepare for addition of deleting destructor - internal changes - rename FB_PROCATTRIB_DESTRUCTOR to FB_PROCATTRIB_DESTRUCTOR1 for the complete destructor - add FB_PROCATTRIB_DESTRUCTOR0 for the deleting destructor - add mangling for complete dtor (D1) and deleting dtor (D0) --- src/compiler/ast-node-proc.bas | 4 ++-- src/compiler/error.bas | 2 +- src/compiler/parser-compound.bas | 6 +++--- src/compiler/parser-proc.bas | 8 ++++++-- src/compiler/rtl-array.bas | 2 +- src/compiler/symb-comp.bas | 6 +++--- src/compiler/symb-mangling.bas | 4 +++- src/compiler/symb-proc.bas | 18 ++++++++++-------- src/compiler/symb.bas | 3 ++- src/compiler/symb.bi | 7 +++++-- 10 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/compiler/ast-node-proc.bas b/src/compiler/ast-node-proc.bas index 9c2b9c974d..6e58111808 100644 --- a/src/compiler/ast-node-proc.bas +++ b/src/compiler/ast-node-proc.bas @@ -500,7 +500,7 @@ sub astProcBegin( byval sym as FBSYMBOL ptr, byval ismain as integer ) astNewVAR( symbGetParamVar( argv ) ) ) '' Destructor? - elseif( symbIsDestructor( sym ) and enable_implicit_code ) then + elseif( symbIsDestructor1( sym ) and enable_implicit_code ) then '' '' If the UDT has a vptr, reset it at the top of destructors, '' such that the vptr always matches the type of object that @@ -673,7 +673,7 @@ function astProcEnd( byval callrtexit as integer ) as integer if( res ) then '' Destructor? - if( symbIsDestructor( sym ) and enable_implicit_code ) then + if( symbIsDestructor1( sym ) and enable_implicit_code ) then '' Call destructors, behind the exit label, so they'll '' always be called, even with early returns. hCallDtors( sym ) diff --git a/src/compiler/error.bas b/src/compiler/error.bas index 0e8d0c4d84..b46471a7ae 100644 --- a/src/compiler/error.bas +++ b/src/compiler/error.bas @@ -878,7 +878,7 @@ private function hMakeParamDesc _ pname = strptr( s ) '' method? elseif( (proc->pattrib and (FB_PROCATTRIB_CONSTRUCTOR or _ - FB_PROCATTRIB_DESTRUCTOR or _ + FB_PROCATTRIB_DESTRUCTOR1 or FB_PROCATTRIB_DESTRUCTOR0 or _ FB_PROCATTRIB_OPERATOR)) <> 0 ) then s = symbMethodToStr( proc ) pname = strptr( s ) diff --git a/src/compiler/parser-compound.bas b/src/compiler/parser-compound.bas index 783420a602..d7a30655f2 100644 --- a/src/compiler/parser-compound.bas +++ b/src/compiler/parser-compound.bas @@ -317,7 +317,7 @@ sub cExitStatement( ) if( symbGetType( parser.currproc ) = FB_DATATYPE_VOID ) then if( (parser.currproc->pattrib and _ (FB_PROCATTRIB_PROPERTY or FB_PROCATTRIB_OPERATOR or _ - FB_PROCATTRIB_CONSTRUCTOR or FB_PROCATTRIB_DESTRUCTOR)) <> 0 ) then + FB_PROCATTRIB_CONSTRUCTOR or FB_PROCATTRIB_DESTRUCTOR1 or FB_PROCATTRIB_DESTRUCTOR0)) <> 0 ) then errnum = FB_ERRMSG_ILLEGALOUTSIDEASUB end if else @@ -328,7 +328,7 @@ sub cExitStatement( ) if( symbGetType( parser.currproc ) <> FB_DATATYPE_VOID ) then if( (parser.currproc->pattrib and _ (FB_PROCATTRIB_PROPERTY or FB_PROCATTRIB_OPERATOR or _ - FB_PROCATTRIB_CONSTRUCTOR or FB_PROCATTRIB_DESTRUCTOR)) <> 0 ) then + FB_PROCATTRIB_CONSTRUCTOR or FB_PROCATTRIB_DESTRUCTOR1 or FB_PROCATTRIB_DESTRUCTOR0)) <> 0 ) then errnum = FB_ERRMSG_ILLEGALOUTSIDEAFUNCTION else errnum = hCheckForCtorResult( ) @@ -357,7 +357,7 @@ sub cExitStatement( ) end if case FB_TK_DESTRUCTOR - if( symbIsDestructor( parser.currproc ) = FALSE ) then + if( symbIsDestructor1( parser.currproc ) = FALSE ) then errnum = FB_ERRMSG_ILLEGALOUTSIDEADTOR end if diff --git a/src/compiler/parser-proc.bas b/src/compiler/parser-proc.bas index c5a8a849ac..9d608848c4 100644 --- a/src/compiler/parser-proc.bas +++ b/src/compiler/parser-proc.bas @@ -1056,7 +1056,9 @@ function cProcHeader _ if( tk = FB_TK_CONSTRUCTOR ) then pattrib or= FB_PROCATTRIB_CONSTRUCTOR or FB_PROCATTRIB_OVERLOADED else - pattrib or= FB_PROCATTRIB_DESTRUCTOR + '' destructor defined by user source is always the complete dtor + '' the deleting dtor is implicitly defined later + pattrib or= FB_PROCATTRIB_DESTRUCTOR1 end if case FB_TK_OPERATOR @@ -1762,7 +1764,9 @@ sub cProcStmtBegin( byval attrib as FB_SYMBATTRIB, byval pattrib as FB_PROCATTRI if( fbLangOptIsSet( FB_LANG_OPT_CLASS ) = FALSE ) then errReportNotAllowed( FB_LANG_OPT_CLASS ) else - pattrib or= FB_PROCATTRIB_DESTRUCTOR + '' destructor defined by user source is always the complete dtor + '' the deleting dtor is implicitly defined later + pattrib or= FB_PROCATTRIB_DESTRUCTOR1 end if hDisallowStaticAttrib( attrib, pattrib ) diff --git a/src/compiler/rtl-array.bas b/src/compiler/rtl-array.bas index 924bf33621..b1d02227c6 100644 --- a/src/compiler/rtl-array.bas +++ b/src/compiler/rtl-array.bas @@ -331,7 +331,7 @@ private sub hCheckDtor _ if( dtor = NULL ) then exit sub - assert( symbIsDestructor( dtor ) ) + assert( symbIsDestructor1( dtor ) ) if( check_access ) then if( symbCheckAccess( dtor ) = FALSE ) then diff --git a/src/compiler/symb-comp.bas b/src/compiler/symb-comp.bas index f953de7180..5c7e57ba4d 100644 --- a/src/compiler/symb-comp.bas +++ b/src/compiler/symb-comp.bas @@ -619,8 +619,8 @@ sub symbUdtDeclareDefaultMembers _ '' no default dtor explicitly defined? if( udt->udt.ext->dtor = NULL ) then - '' Dtor - default.dtor = hDeclareProc( udt, INVALID, FB_DATATYPE_INVALID, FB_SYMBATTRIB_NONE, FB_PROCATTRIB_DESTRUCTOR ) + '' Complete Dtor + default.dtor = hDeclareProc( udt, INVALID, FB_DATATYPE_INVALID, FB_SYMBATTRIB_NONE, FB_PROCATTRIB_DESTRUCTOR1 ) '' Don't allow the implicit dtor to override a FINAL dtor from the base symbProcCheckOverridden( default.dtor, TRUE ) @@ -785,7 +785,7 @@ end sub sub symbSetCompDtor( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) if( symbIsStruct( sym ) ) then - assert( symbIsDestructor( proc ) ) + assert( symbIsDestructor1( proc ) ) symbUdtAllocExt( sym ) if( sym->udt.ext->dtor = NULL ) then '' Add dtor diff --git a/src/compiler/symb-mangling.bas b/src/compiler/symb-mangling.bas index edad015795..a6b38dce49 100644 --- a/src/compiler/symb-mangling.bas +++ b/src/compiler/symb-mangling.bas @@ -1278,7 +1278,9 @@ private sub hMangleProc( byval sym as FBSYMBOL ptr ) end if elseif( symbIsConstructor( sym ) ) then mangled += "C1" - elseif( symbIsDestructor( sym ) ) then + elseif( symbIsDestructor0( sym ) ) then + mangled += "D0" + elseif( symbIsDestructor1( sym ) ) then mangled += "D1" else if( symbGetMangling( sym ) = FB_MANGLING_BASIC ) then diff --git a/src/compiler/symb-proc.bas b/src/compiler/symb-proc.bas index 9af62f8f2c..8b97aef6c1 100644 --- a/src/compiler/symb-proc.bas +++ b/src/compiler/symb-proc.bas @@ -716,7 +716,7 @@ private function hSetupProc _ '' ctor/dtor? if( (pattrib and (FB_PROCATTRIB_CONSTRUCTOR or _ - FB_PROCATTRIB_DESTRUCTOR)) <> 0 ) then + FB_PROCATTRIB_DESTRUCTOR1 or FB_PROCATTRIB_DESTRUCTOR0)) <> 0 ) then assert( pattrib and FB_PROCATTRIB_METHOD ) @@ -748,7 +748,7 @@ private function hSetupProc _ '' otherwise, try to overload else '' dtor? - if( (pattrib and FB_PROCATTRIB_DESTRUCTOR) <> 0 ) then + if( (pattrib and (FB_PROCATTRIB_DESTRUCTOR1 or FB_PROCATTRIB_DESTRUCTOR0)) <> 0 ) then '' can't overload return NULL end if @@ -920,8 +920,8 @@ add_proc: overridden = NULL if( parent->udt.base ) then '' Destructor? - if( symbIsDestructor( proc ) ) then - '' There can always only be one, so there is no + if( symbIsDestructor1( proc ) ) then + '' There can always only be one complete dtor, so there is no '' need to do a lookup and/or overload checks. overridden = symbGetCompDtor( parent->udt.base->subtype ) elseif( symbIsOperator( proc ) ) then @@ -1601,7 +1601,7 @@ function symbFindCtorProc _ ) as FBSYMBOL ptr '' dtor? can't overload.. - if( symbIsDestructor( ovl_head_proc ) ) then + if( symbIsDestructor1( ovl_head_proc ) ) then return ovl_head_proc else return symbFindOverloadProc( ovl_head_proc, proc ) @@ -2845,7 +2845,7 @@ sub symbProcCheckOverridden _ '' implicit dtors and LET overloads. Since they '' are not visible in the original code, '' the error message must have more info. - if( symbIsDestructor( proc ) ) then + if( symbIsDestructor1( proc ) ) then errmsg = FB_ERRMSG_IMPLICITDTOROVERRIDECALLCONVDIFFERS else errmsg = FB_ERRMSG_IMPLICITLETOVERRIDECALLCONVDIFFERS @@ -2916,7 +2916,7 @@ end sub '' Append calling convention, if it differs from the default private sub hProcModeToStr( byref s as string, byval proc as FBSYMBOL ptr ) '' Ctors/Dtors currently always default to CDECL, see cProcHeader() - if( symbIsConstructor( proc ) or symbIsDestructor( proc ) ) then + if( symbIsConstructor( proc ) or symbIsDestructor1( proc ) or symbIsDestructor0( proc ) ) then select case( symbGetProcMode( proc ) ) case FB_FUNCMODE_STDCALL, FB_FUNCMODE_STDCALL_MS s += " stdcall" @@ -3045,7 +3045,9 @@ function symbGetFullProcName( byval proc as FBSYMBOL ptr ) as zstring ptr if( symbIsConstructor( proc ) ) then res += "constructor" - elseif( symbIsDestructor( proc ) ) then + elseif( symbIsDestructor1( proc ) ) then + res += "destructor" + elseif( symbIsDestructor0( proc ) ) then res += "destructor" elseif( symbIsOperator( proc ) ) then res += "operator." diff --git a/src/compiler/symb.bas b/src/compiler/symb.bas index 751a0cee2f..5d467d3c32 100644 --- a/src/compiler/symb.bas +++ b/src/compiler/symb.bas @@ -2506,7 +2506,8 @@ function symbDumpToStr _ checkPAttrib( OVERLOADED ) checkPAttrib( METHOD ) checkPAttrib( CONSTRUCTOR ) - checkPAttrib( DESTRUCTOR ) + checkPAttrib( DESTRUCTOR1 ) + checkPAttrib( DESTRUCTOR0 ) checkPAttrib( OPERATOR ) checkPAttrib( PROPERTY ) checkPAttrib( RETURNBYREF ) diff --git a/src/compiler/symb.bi b/src/compiler/symb.bi index 7aa6d5dde2..3c8d73b4f8 100644 --- a/src/compiler/symb.bi +++ b/src/compiler/symb.bi @@ -191,7 +191,7 @@ enum FB_PROCATTRIB FB_PROCATTRIB_OVERLOADED = &h00000001 '' PROCs / METHODs only FB_PROCATTRIB_METHOD = &h00000002 '' Non-STATIC UDT member procs, i.e. procs with implicit THIS ptr FB_PROCATTRIB_CONSTRUCTOR = &h00000004 '' methods only - FB_PROCATTRIB_DESTRUCTOR = &h00000008 '' methods only + FB_PROCATTRIB_DESTRUCTOR1 = &h00000008 '' methods only FB_PROCATTRIB_OPERATOR = &h00000010 '' methods only FB_PROCATTRIB_PROPERTY = &h00000020 '' methods only FB_PROCATTRIB_STATICLOCALS = &h00000040 '' PROCs only @@ -200,6 +200,7 @@ enum FB_PROCATTRIB FB_PROCATTRIB_ABSTRACT = &h00000200 '' methods only: pure virtuals (only) FB_PROCATTRIB_NOTHISCONSTNESS = &h00000400 '' PROCs only FB_PROCATTRIB_RETURNBYREF = &h00000800 '' PROCs (functions/function pointers returning BYREF) + FB_PROCATTRIB_DESTRUCTOR0 = &h00001000 '' methods only - deleting destructor end enum '' parameter modes @@ -2498,7 +2499,9 @@ declare sub symbProcRecalcRealType( byval proc as FBSYMBOL ptr ) #define symbIsConstructor(s) ((s->pattrib and FB_PROCATTRIB_CONSTRUCTOR) <> 0) -#define symbIsDestructor(s) ((s->pattrib and FB_PROCATTRIB_DESTRUCTOR) <> 0) +#define symbIsDestructor0(s) ((s->pattrib and FB_PROCATTRIB_DESTRUCTOR0) <> 0) + +#define symbIsDestructor1(s) ((s->pattrib and FB_PROCATTRIB_DESTRUCTOR1) <> 0) #define symbIsOperator(s) ((s->pattrib and FB_PROCATTRIB_OPERATOR) <> 0) From acc219ff1ce974fea3c2be266cb99c9abcf4926a Mon Sep 17 00:00:00 2001 From: coderJeff Date: Thu, 29 Apr 2021 23:27:31 -0400 Subject: [PATCH 02/14] fbc: g++ ABI - whitespace changes before next update --- src/compiler/symb.bi | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/compiler/symb.bi b/src/compiler/symb.bi index 3c8d73b4f8..12943e6fe5 100644 --- a/src/compiler/symb.bi +++ b/src/compiler/symb.bi @@ -486,18 +486,18 @@ type FB_STRUCT_DBG end type type FB_STRUCTEXT - ctorhead as FBSYMBOL_ ptr '' ctor head (first overload proc) or NULL - defctor as FBSYMBOL_ ptr '' default ctor or NULL - copyctor as FBSYMBOL_ ptr '' copy ctor or NULL - copyctorconst as FBSYMBOL_ ptr '' copy ctor with CONST param or NULL - dtor as FBSYMBOL_ ptr '' destructor or NULL - copyletop as FBSYMBOL_ ptr '' copy LET overload proc or NULL - copyletopconst as FBSYMBOL_ ptr '' copy LET overload proc with CONST param or NULL + ctorhead as FBSYMBOL_ ptr '' ctor head (first overload proc) or NULL + defctor as FBSYMBOL_ ptr '' default ctor or NULL + copyctor as FBSYMBOL_ ptr '' copy ctor or NULL + copyctorconst as FBSYMBOL_ ptr '' copy ctor with CONST param or NULL + dtor as FBSYMBOL_ ptr '' destructor or NULL + copyletop as FBSYMBOL_ ptr '' copy LET overload proc or NULL + copyletopconst as FBSYMBOL_ ptr '' copy LET overload proc with CONST param or NULL opovlTb(0 to AST_OP_SELFOPS-1) as FBSYMBOL_ ptr - vtableelements as integer '' vtable elements counter - vtable as FBSYMBOL_ ptr '' virtual-functions table struct - rtti as FBSYMBOL_ ptr '' Run-Time Type Info struct - abstractcount as integer '' ABSTRACT method counter (to determine abstract classes, which aren't allowed to be instantiated) + vtableelements as integer '' vtable elements counter + vtable as FBSYMBOL_ ptr '' virtual-functions table struct + rtti as FBSYMBOL_ ptr '' Run-Time Type Info struct + abstractcount as integer '' ABSTRACT method counter (to determine abstract classes, which aren't allowed to be instantiated) end type type FBS_STRUCT @@ -917,11 +917,11 @@ end type '' Data passed from symbUdtDeclareDefaultMembers() to symbUdtImplementDefaultMembers() type SYMBDEFAULTMEMBERS - defctor as FBSYMBOL ptr - dtor as FBSYMBOL ptr - copyctor as FBSYMBOL ptr - copyctorconst as FBSYMBOL ptr - copyletopconst as FBSYMBOL ptr + defctor as FBSYMBOL ptr + dtor as FBSYMBOL ptr + copyctor as FBSYMBOL ptr + copyctorconst as FBSYMBOL ptr + copyletopconst as FBSYMBOL ptr end type #include once "ast.bi" From 93178357f1d3c3cacf21da3cedf5d466e6149f36 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Thu, 29 Apr 2021 23:39:52 -0400 Subject: [PATCH 03/14] fbc: g++ ABI - add tracking for complete and deleting destructors - internal changes - FB_STRUCTEXT - rename dtor to dtor1 and add dtor0 - SYMBDEFAULTMEMBERS - rename dtor to dtor1 and add dtor0 - symbSetCompDtor() - rename to symbSetCompDtor1() and add symbSetCompDtor0() - symbGetCompDtor() - rename to symbGetCompDtor1() and add symbGetCompDtor0() --- src/compiler/ast-helper.bas | 4 +- src/compiler/ast-node-proc.bas | 4 +- src/compiler/parser-decl-struct.bas | 2 +- src/compiler/parser-decl-var.bas | 2 +- src/compiler/parser-expr-variable.bas | 2 +- src/compiler/parser-proc.bas | 2 +- src/compiler/parser-quirk-mem.bas | 2 +- src/compiler/rtl-array.bas | 6 +-- src/compiler/symb-comp.bas | 58 +++++++++++++++++++++------ src/compiler/symb-data.bas | 2 +- src/compiler/symb-proc.bas | 17 ++++++-- src/compiler/symb-struct.bas | 2 +- src/compiler/symb.bi | 12 ++++-- 13 files changed, 81 insertions(+), 34 deletions(-) diff --git a/src/compiler/ast-helper.bas b/src/compiler/ast-helper.bas index 3aee36492b..a0e2268a62 100644 --- a/src/compiler/ast-helper.bas +++ b/src/compiler/ast-helper.bas @@ -153,7 +153,7 @@ function astBuildVarDtorCall overload _ '' UDT var with dtor? if( symbHasDtor( s ) ) then if( check_access ) then - if( symbCheckAccess( symbGetCompDtor( symbGetSubtype( s ) ) ) = FALSE ) then + if( symbCheckAccess( symbGetCompDtor1( symbGetSubtype( s ) ) ) = FALSE ) then errReport( FB_ERRMSG_NOACCESSTODTOR ) end if end if @@ -543,7 +543,7 @@ function astBuildDtorCall _ dim as ASTNODE ptr callexpr = any '' Can be virtual - dtor = symbGetCompDtor( sym ) + dtor = symbGetCompDtor1( sym ) if( ignore_virtual ) then callexpr = astNewCALL( dtor ) else diff --git a/src/compiler/ast-node-proc.bas b/src/compiler/ast-node-proc.bas index 6e58111808..0c7079fa99 100644 --- a/src/compiler/ast-node-proc.bas +++ b/src/compiler/ast-node-proc.bas @@ -500,7 +500,7 @@ sub astProcBegin( byval sym as FBSYMBOL ptr, byval ismain as integer ) astNewVAR( symbGetParamVar( argv ) ) ) '' Destructor? - elseif( symbIsDestructor1( sym ) and enable_implicit_code ) then + elseif( (symbIsDestructor0( sym ) or symbIsDestructor1( sym )) and enable_implicit_code ) then '' '' If the UDT has a vptr, reset it at the top of destructors, '' such that the vptr always matches the type of object that @@ -1296,7 +1296,7 @@ private sub hCallBaseDtor _ '' Just like derived classes are not responsible for initializing their '' base class, they shouldn't be made responsible for cleaning it up. - dtor = symbGetCompDtor( symbGetSubtype( base_ ) ) + dtor = symbGetCompDtor1( symbGetSubtype( base_ ) ) if( dtor = NULL ) then exit sub end if diff --git a/src/compiler/parser-decl-struct.bas b/src/compiler/parser-decl-struct.bas index 3affe7e3ac..f28d1041ca 100644 --- a/src/compiler/parser-decl-struct.bas +++ b/src/compiler/parser-decl-struct.bas @@ -183,7 +183,7 @@ private sub hFieldInit _ end if '' Does it have a destructor? - defctor = symbGetCompDtor( subtype ) + defctor = symbGetCompDtor1( subtype ) if( defctor ) then '' Check whether we have access if( symbCheckAccess( defctor ) = FALSE ) then diff --git a/src/compiler/parser-decl-var.bas b/src/compiler/parser-decl-var.bas index 16733fa150..38134aec37 100644 --- a/src/compiler/parser-decl-var.bas +++ b/src/compiler/parser-decl-var.bas @@ -1189,7 +1189,7 @@ private function hFlushInitializer _ '' object? if( has_dtor and (not symbIsRef( sym )) ) then '' check visibility - if( symbCheckAccess( symbGetCompDtor( symbGetSubtype( sym ) ) ) = FALSE ) then + if( symbCheckAccess( symbGetCompDtor1( symbGetSubtype( sym ) ) ) = FALSE ) then errReport( FB_ERRMSG_NOACCESSTODTOR ) end if end if diff --git a/src/compiler/parser-expr-variable.bas b/src/compiler/parser-expr-variable.bas index ac3f94c52e..a670e9c7a7 100644 --- a/src/compiler/parser-expr-variable.bas +++ b/src/compiler/parser-expr-variable.bas @@ -246,7 +246,7 @@ private function hMemberId( byval parent as FBSYMBOL ptr ) as FBSYMBOL ptr case FB_TK_CONSTRUCTOR res = symbGetCompCtorHead( parent ) case FB_TK_DESTRUCTOR - res = symbGetCompDtor( parent ) + res = symbGetCompDtor1( parent ) end select if( res ) then diff --git a/src/compiler/parser-proc.bas b/src/compiler/parser-proc.bas index 9d608848c4..9bc470c262 100644 --- a/src/compiler/parser-proc.bas +++ b/src/compiler/parser-proc.bas @@ -1556,7 +1556,7 @@ function cProcHeader _ case FB_TK_CONSTRUCTOR head_proc = symbGetCompCtorHead( parent ) case FB_TK_DESTRUCTOR - head_proc = symbGetCompDtor( parent ) + head_proc = symbGetCompDtor1( parent ) case FB_TK_OPERATOR head_proc = symbGetCompOpOvlHead( parent, op ) end select diff --git a/src/compiler/parser-quirk-mem.bas b/src/compiler/parser-quirk-mem.bas index 678880f45f..6caa967635 100644 --- a/src/compiler/parser-quirk-mem.bas +++ b/src/compiler/parser-quirk-mem.bas @@ -260,7 +260,7 @@ sub cOperatorDelete( ) '' check visibility if( typeHasDtor( typeDeref( dtype ), subtype ) ) then - if( symbCheckAccess( symbGetCompDtor( subtype ) ) = FALSE ) then + if( symbCheckAccess( symbGetCompDtor1( subtype ) ) = FALSE ) then errReport( FB_ERRMSG_NOACCESSTODTOR ) end if end if diff --git a/src/compiler/rtl-array.bas b/src/compiler/rtl-array.bas index b1d02227c6..745f3a3e33 100644 --- a/src/compiler/rtl-array.bas +++ b/src/compiler/rtl-array.bas @@ -370,7 +370,7 @@ function rtlArrayClear( byval arrayexpr as ASTNODE ptr ) as ASTNODE ptr if( dtype = FB_DATATYPE_STRUCT ) then subtype = astGetSubtype( arrayexpr ) ctor = symbGetCompDefCtor( subtype ) - dtor = symbGetCompDtor( subtype ) + dtor = symbGetCompDtor1( subtype ) '' No default ctor, but others? Then the rtlib cannot just clear '' that array of objects. @@ -453,7 +453,7 @@ function rtlArrayErase _ if( dtype = FB_DATATYPE_STRUCT ) then subtype = astGetSubtype( arrayexpr ) ctor = symbGetCompDefCtor( subtype ) - dtor = symbGetCompDtor( subtype ) + dtor = symbGetCompDtor1( subtype ) '' No default ctor, but others? Then the rtlib cannot just clear '' that array of objects. @@ -534,7 +534,7 @@ private sub hGetCtorDtorForRedim _ if( typeGetDtAndPtrOnly( dtype ) = FB_DATATYPE_STRUCT ) then ctor = symbGetCompDefCtor( subtype ) - dtor = symbGetCompDtor( subtype ) + dtor = symbGetCompDtor1( subtype ) '' Assuming there aren't any other ctors if there is no default one, '' because if it were possible to declare such a dynamic array, diff --git a/src/compiler/symb-comp.bas b/src/compiler/symb-comp.bas index 5c7e57ba4d..ed26949bbf 100644 --- a/src/compiler/symb-comp.bas +++ b/src/compiler/symb-comp.bas @@ -559,7 +559,8 @@ sub symbUdtDeclareDefaultMembers _ default.copyctor = NULL default.copyctorconst = NULL default.copyletopconst = NULL - default.dtor = NULL + default.dtor1 = NULL + default.dtor0 = NULL '' Ctor/inited fields and no ctor yet? if( (symbGetUDTHasCtorField( udt ) or symbGetUDTHasInitedField( udt )) and _ @@ -618,12 +619,18 @@ sub symbUdtDeclareDefaultMembers _ symbUdtAllocExt( udt ) '' no default dtor explicitly defined? - if( udt->udt.ext->dtor = NULL ) then - '' Complete Dtor - default.dtor = hDeclareProc( udt, INVALID, FB_DATATYPE_INVALID, FB_SYMBATTRIB_NONE, FB_PROCATTRIB_DESTRUCTOR1 ) + if( udt->udt.ext->dtor1 = NULL ) then + + assert( udt->udt.ext->dtor0 = NULL ) + + '' Complete dtor + default.dtor1 = hDeclareProc( udt, INVALID, FB_DATATYPE_INVALID, FB_SYMBATTRIB_NONE, FB_PROCATTRIB_DESTRUCTOR1 ) + + '' Deleting dtor + default.dtor0 = hDeclareProc( udt, INVALID, FB_DATATYPE_INVALID, FB_SYMBATTRIB_NONE, FB_PROCATTRIB_DESTRUCTOR0 ) '' Don't allow the implicit dtor to override a FINAL dtor from the base - symbProcCheckOverridden( default.dtor, TRUE ) + symbProcCheckOverridden( default.dtor1, TRUE ) end if end if @@ -685,8 +692,12 @@ sub symbUdtImplementDefaultMembers _ hAddCtorBody( udt, default.copyctor, TRUE ) end if - if( default.dtor ) then - hAddCtorBody( udt, default.dtor, FALSE ) + if( default.dtor1 ) then + hAddCtorBody( udt, default.dtor1, FALSE ) + end if + + if( default.dtor0 ) then + hAddCtorBody( udt, default.dtor0, FALSE ) end if end sub @@ -733,7 +744,7 @@ end function function symbCompIsTrivial( byval sym as FBSYMBOL ptr ) as integer assert( symbIsStruct( sym ) ) function = ((symbGetCompCopyCtor( sym ) = NULL) and _ - (symbGetCompDtor( sym ) = NULL) and _ + (symbGetCompDtor1( sym ) = NULL) and _ (not symbGetHasRTTI( sym ))) end function @@ -783,13 +794,24 @@ sub symbCheckCompCtor( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) end if end sub -sub symbSetCompDtor( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) +sub symbSetCompDtor1( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) if( symbIsStruct( sym ) ) then assert( symbIsDestructor1( proc ) ) symbUdtAllocExt( sym ) - if( sym->udt.ext->dtor = NULL ) then + if( sym->udt.ext->dtor1 = NULL ) then + '' Add dtor + sym->udt.ext->dtor1 = proc + end if + end if +end sub + +sub symbSetCompDtor0( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) + if( symbIsStruct( sym ) ) then + assert( symbIsDestructor0( proc ) ) + symbUdtAllocExt( sym ) + if( sym->udt.ext->dtor0 = NULL ) then '' Add dtor - sym->udt.ext->dtor = proc + sym->udt.ext->dtor0 = proc end if end if end sub @@ -821,11 +843,21 @@ private function symbGetCompCopyCtor( byval sym as FBSYMBOL ptr ) as FBSYMBOL pt end if end function -function symbGetCompDtor( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr +function symbGetCompDtor1( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr + if( sym ) then + if( symbIsStruct( sym ) ) then + if( sym->udt.ext ) then + function = sym->udt.ext->dtor1 + end if + end if + end if +end function + +function symbGetCompDtor0( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr if( sym ) then if( symbIsStruct( sym ) ) then if( sym->udt.ext ) then - function = sym->udt.ext->dtor + function = sym->udt.ext->dtor0 end if end if end if diff --git a/src/compiler/symb-data.bas b/src/compiler/symb-data.bas index b3f54e4dc4..02c8b2f08a 100644 --- a/src/compiler/symb-data.bas +++ b/src/compiler/symb-data.bas @@ -322,7 +322,7 @@ function typeHasDtor _ select case( typeGet( dtype ) ) case FB_DATATYPE_STRUCT '', FB_DATATYPE_CLASS - function = (symbGetCompDtor( subtype ) <> NULL) + function = (symbGetCompDtor1( subtype ) <> NULL) end select end function diff --git a/src/compiler/symb-proc.bas b/src/compiler/symb-proc.bas index 8b97aef6c1..aac18f9122 100644 --- a/src/compiler/symb-proc.bas +++ b/src/compiler/symb-proc.bas @@ -729,8 +729,10 @@ private function hSetupProc _ '' ctor? if( (pattrib and FB_PROCATTRIB_CONSTRUCTOR) <> 0 ) then head_proc = symbGetCompCtorHead( parent ) + elseif( (pattrib and FB_PROCATTRIB_DESTRUCTOR1) <> 0 ) then + head_proc = symbGetCompDtor1( parent ) else - head_proc = symbGetCompDtor( parent ) + head_proc = symbGetCompDtor0( parent ) end if '' not overloaded yet? just add it @@ -739,12 +741,21 @@ private function hSetupProc _ FB_SYMBCLASS_PROC, NULL, id_alias, _ FB_DATATYPE_VOID, NULL, attrib, pattrib ) + '' ctor? if( (pattrib and FB_PROCATTRIB_CONSTRUCTOR) <> 0 ) then symbSetCompCtorHead( parent, proc ) + + '' complete dtor? + elseif( (pattrib and FB_PROCATTRIB_DESTRUCTOR1) <> 0 ) then + symbSetCompDtor1( parent, proc ) + + '' deleting dtor else - symbSetCompDtor( parent, proc ) + symbSetCompDtor0( parent, proc ) + end if + '' otherwise, try to overload else '' dtor? @@ -923,7 +934,7 @@ add_proc: if( symbIsDestructor1( proc ) ) then '' There can always only be one complete dtor, so there is no '' need to do a lookup and/or overload checks. - overridden = symbGetCompDtor( parent->udt.base->subtype ) + overridden = symbGetCompDtor1( parent->udt.base->subtype ) elseif( symbIsOperator( proc ) ) then '' Get the corresponding operator from the base '' (actually a chain of overloads for that particular operator) diff --git a/src/compiler/symb-struct.bas b/src/compiler/symb-struct.bas index ecd855b140..4eb9ed4561 100644 --- a/src/compiler/symb-struct.bas +++ b/src/compiler/symb-struct.bas @@ -502,7 +502,7 @@ function symbAddField _ end if end if - if( symbGetCompDtor( subtype ) ) then + if( symbGetCompDtor1( subtype ) ) then '' not allowed inside unions or anonymous nested structs/unions if( symbGetUDTIsUnionOrAnon( parent ) ) then errReport( FB_ERRMSG_DTORINUNION ) diff --git a/src/compiler/symb.bi b/src/compiler/symb.bi index 12943e6fe5..c958c1443c 100644 --- a/src/compiler/symb.bi +++ b/src/compiler/symb.bi @@ -490,7 +490,8 @@ type FB_STRUCTEXT defctor as FBSYMBOL_ ptr '' default ctor or NULL copyctor as FBSYMBOL_ ptr '' copy ctor or NULL copyctorconst as FBSYMBOL_ ptr '' copy ctor with CONST param or NULL - dtor as FBSYMBOL_ ptr '' destructor or NULL + dtor1 as FBSYMBOL_ ptr '' complete object destructor or NULL + dtor0 as FBSYMBOL_ ptr '' deleting destructor or NULL copyletop as FBSYMBOL_ ptr '' copy LET overload proc or NULL copyletopconst as FBSYMBOL_ ptr '' copy LET overload proc with CONST param or NULL opovlTb(0 to AST_OP_SELFOPS-1) as FBSYMBOL_ ptr @@ -918,7 +919,8 @@ end type '' Data passed from symbUdtDeclareDefaultMembers() to symbUdtImplementDefaultMembers() type SYMBDEFAULTMEMBERS defctor as FBSYMBOL ptr - dtor as FBSYMBOL ptr + dtor1 as FBSYMBOL ptr + dtor0 as FBSYMBOL ptr copyctor as FBSYMBOL ptr copyctorconst as FBSYMBOL ptr copyletopconst as FBSYMBOL ptr @@ -1885,10 +1887,12 @@ declare sub symbUdtImplementDefaultMembers _ declare function symbCompIsTrivial( byval sym as FBSYMBOL ptr ) as integer declare sub symbSetCompCtorHead( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) declare sub symbCheckCompCtor( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) -declare sub symbSetCompDtor( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) +declare sub symbSetCompDtor1( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) +declare sub symbSetCompDtor0( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) declare function symbGetCompCtorHead( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr declare function symbGetCompDefCtor( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr -declare function symbGetCompDtor( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr +declare function symbGetCompDtor1( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr +declare function symbGetCompDtor0( byval sym as FBSYMBOL ptr ) as FBSYMBOL ptr declare sub symbCheckCompLetOp( byval sym as FBSYMBOL ptr, byval proc as FBSYMBOL ptr ) declare function symbCompHasCopyLetOps( byval udt as FBSYMBOL ptr ) as integer From 947a9f49181c16628356e2e1b6b88f07457d516f Mon Sep 17 00:00:00 2001 From: coderJeff Date: Thu, 29 Apr 2021 23:44:36 -0400 Subject: [PATCH 04/14] fbc: g++ ABI - emit the deleting destructor - emit the deleting destructor only in c++ mangling - if type is in an extern "c++" block, implicitly generate and emit the deleting destructor (D0) --- changelog.txt | 1 + src/compiler/ast-node-proc.bas | 28 +++++++++++++++++++++++++++- src/compiler/parser-proc.bas | 15 +++++++++++++++ src/compiler/symb-comp.bas | 15 +++++++++++++-- src/compiler/symb-proc.bas | 5 +---- src/compiler/symb.bi | 2 +- 6 files changed, 58 insertions(+), 8 deletions(-) diff --git a/changelog.txt b/changelog.txt index 8c11fa09e7..faff6f6dd2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -46,6 +46,7 @@ Version 1.08.0 - only write debug line information for statements and don't write comments / empty lines / directives for top level source code in assembly debug ouput - optimize byref 'm += s' string concatenations to fb_StrConcatByref() which will check for same string descriptor at run-time which can't be determined at compile time for byref parameters. - github #298: allow command line options passed to as, gcc, ld to be longer than 128 characters by using string types internally +- sf.net #923: implicitly emit the deleting destrutor for extern "c++" mangling for better g++ ABI compatibility [added] - extern "rtlib": respects the parent namespace, uses default fb calling convention and C style name mangling diff --git a/src/compiler/ast-node-proc.bas b/src/compiler/ast-node-proc.bas index 0c7079fa99..f8bf326776 100644 --- a/src/compiler/ast-node-proc.bas +++ b/src/compiler/ast-node-proc.bas @@ -33,6 +33,7 @@ declare function hInitVptr _ ) as ASTNODE ptr declare sub hCallCtors( byval n as ASTNODE ptr, byval sym as FBSYMBOL ptr ) declare sub hCallDtors( byval proc as FBSYMBOL ptr ) +declare sub hCallDeleteDtor( byval proc as FBSYMBOL ptr ) declare sub hGenStaticInstancesDtors( byval proc as FBSYMBOL ptr ) declare sub hGenGlobalInstancesCtor( ) @@ -672,13 +673,18 @@ function astProcEnd( byval callrtexit as integer ) as integer res = (symbCheckLabels(symbGetProcSymbTbHead(parser.currproc)) = 0) if( res ) then - '' Destructor? + '' Complete Destructor? if( symbIsDestructor1( sym ) and enable_implicit_code ) then '' Call destructors, behind the exit label, so they'll '' always be called, even with early returns. hCallDtors( sym ) end if + '' Deleting Destructor? + if( symbIsDestructor0( sym ) and enable_implicit_code ) then + hCallDeleteDtor( sym ) + end if + '' update proc's breaks list, adding calls to destructors when needed if( n->block.breaklist.head <> NULL ) then res = astScopeUpdBreakList( n ) @@ -1272,6 +1278,26 @@ private sub hCallFieldDtors _ end sub +private sub hCallDeleteDtor _ + ( _ + byval proc as FBSYMBOL ptr _ + ) + + dim as FBSYMBOL ptr parent = any, dtor1 = any, this_ = any + dim as ASTNODE ptr thisptr = any, tree = any + + parent = symbGetNamespace( proc ) + dtor1 = symbGetCompDtor1( parent ) + if( dtor1 = NULL ) then + exit sub + end if + this_ = symbGetParamVar( symbGetProcHeadParam( proc ) ) + thisptr = astNewADDROF( astBuildVarField( this_ ) ) + tree = astBuildDeleteOp( AST_OP_DEL, thisptr ) + astAdd( tree ) + +end sub + private sub hCallBaseDtor _ ( _ byval parent as FBSYMBOL ptr, _ diff --git a/src/compiler/parser-proc.bas b/src/compiler/parser-proc.bas index 9bc470c262..ec47a82441 100644 --- a/src/compiler/parser-proc.bas +++ b/src/compiler/parser-proc.bas @@ -1466,6 +1466,21 @@ function cProcHeader _ cOverrideAttribute( proc ) end if + '' destructor? maybe implicitly declare the deleting destructor too + if( tk = FB_TK_DESTRUCTOR ) then + '' fbc won't generate any code that calls the deleting destructor + '' so don't create the deleting destructor unless we're binding to c++ + if( symbGetMangling( parent ) = FB_MANGLING_CPP ) then + '' - inherit all the attribs from the declared destructor + '' except for the destructor type + dim dtor0 as FBSYMBOL ptr = symbPreAddProc( NULL ) + symbAddProcInstanceParam( parent, dtor0 ) + pattrib and= not FB_PROCATTRIB_DESTRUCTOR1 + pattrib or= FB_PROCATTRIB_DESTRUCTOR0 + dtor0 = symbAddCtor( dtor0, NULL, attrib, pattrib, mode ) + end if + end if + if( tk = FB_TK_PROPERTY ) then hSetUdtPropertyFlags( parent, is_indexed, is_get ) end if diff --git a/src/compiler/symb-comp.bas b/src/compiler/symb-comp.bas index ed26949bbf..8d7a845424 100644 --- a/src/compiler/symb-comp.bas +++ b/src/compiler/symb-comp.bas @@ -621,19 +621,30 @@ sub symbUdtDeclareDefaultMembers _ '' no default dtor explicitly defined? if( udt->udt.ext->dtor1 = NULL ) then + '' if we didn't have the complete dtor then we shouln't have the deleting dtor either + '' because there is no way to explicitly define the deleting dtor assert( udt->udt.ext->dtor0 = NULL ) '' Complete dtor default.dtor1 = hDeclareProc( udt, INVALID, FB_DATATYPE_INVALID, FB_SYMBATTRIB_NONE, FB_PROCATTRIB_DESTRUCTOR1 ) - '' Deleting dtor - default.dtor0 = hDeclareProc( udt, INVALID, FB_DATATYPE_INVALID, FB_SYMBATTRIB_NONE, FB_PROCATTRIB_DESTRUCTOR0 ) + if( symbGetMangling( udt ) = FB_MANGLING_CPP ) then + '' Deleting dtor + default.dtor0 = hDeclareProc( udt, INVALID, FB_DATATYPE_INVALID, FB_SYMBATTRIB_NONE, FB_PROCATTRIB_DESTRUCTOR0 ) + end if '' Don't allow the implicit dtor to override a FINAL dtor from the base symbProcCheckOverridden( default.dtor1, TRUE ) end if end if + if( symbGetMangling( udt ) = FB_MANGLING_CPP ) then + if( default.dtor0 = NULL ) then + '' deleting dtor will be implicitly declared if the complete dtor was explicitly declared + default.dtor0 = symbGetCompDtor0( udt ) + end if + end if + end sub '' Implement the implicit members declared by symbUdtDeclareDefaultMembers(), diff --git a/src/compiler/symb-proc.bas b/src/compiler/symb-proc.bas index aac18f9122..a0e812c9d5 100644 --- a/src/compiler/symb-proc.bas +++ b/src/compiler/symb-proc.bas @@ -741,7 +741,6 @@ private function hSetupProc _ FB_SYMBCLASS_PROC, NULL, id_alias, _ FB_DATATYPE_VOID, NULL, attrib, pattrib ) - '' ctor? if( (pattrib and FB_PROCATTRIB_CONSTRUCTOR) <> 0 ) then symbSetCompCtorHead( parent, proc ) @@ -753,9 +752,7 @@ private function hSetupProc _ '' deleting dtor else symbSetCompDtor0( parent, proc ) - end if - '' otherwise, try to overload else '' dtor? @@ -1612,7 +1609,7 @@ function symbFindCtorProc _ ) as FBSYMBOL ptr '' dtor? can't overload.. - if( symbIsDestructor1( ovl_head_proc ) ) then + if( symbIsDestructor1( ovl_head_proc ) or symbIsDestructor0( ovl_head_proc ) ) then return ovl_head_proc else return symbFindOverloadProc( ovl_head_proc, proc ) diff --git a/src/compiler/symb.bi b/src/compiler/symb.bi index c958c1443c..575141d23f 100644 --- a/src/compiler/symb.bi +++ b/src/compiler/symb.bi @@ -191,7 +191,7 @@ enum FB_PROCATTRIB FB_PROCATTRIB_OVERLOADED = &h00000001 '' PROCs / METHODs only FB_PROCATTRIB_METHOD = &h00000002 '' Non-STATIC UDT member procs, i.e. procs with implicit THIS ptr FB_PROCATTRIB_CONSTRUCTOR = &h00000004 '' methods only - FB_PROCATTRIB_DESTRUCTOR1 = &h00000008 '' methods only + FB_PROCATTRIB_DESTRUCTOR1 = &h00000008 '' methods only - complete destructor FB_PROCATTRIB_OPERATOR = &h00000010 '' methods only FB_PROCATTRIB_PROPERTY = &h00000020 '' methods only FB_PROCATTRIB_STATICLOCALS = &h00000040 '' PROCs only From 39aee2640d4b938e4daf8319fdd75ff5548db758 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Sat, 7 Nov 2020 21:11:53 -0500 Subject: [PATCH 05/14] fbc: add "__thiscall" calling convention, -gen gcc only - adds the keyword '__thiscall' - emits '__thiscall' or __'attribute__((thiscall))' in -gen gcc - emits x86_thiscallcc in -gen llvm (not tested) - ignored in -gen gas, (i.e. no change in emitted code even if thiscall is present) - must be explicitly specified in the procedure declaration - default use of "thiscall" calling convention still platform dependant (e.g. win x86 32-bit) compared to default usage in g++ - recommend user to '#define thiscall __thiscall' depending on target platform --- changelog.txt | 1 + src/compiler/ast-node-call.bas | 1 + src/compiler/fbint.bi | 1 + src/compiler/ir-hlc.bas | 7 +++++++ src/compiler/ir-llvm.bas | 2 ++ src/compiler/parser-proc.bas | 6 +++++- src/compiler/symb-keyword.bas | 1 + src/compiler/symb-proc.bas | 4 ++++ src/compiler/symb.bas | 1 + src/compiler/symb.bi | 1 + todo.txt | 10 ++++++++++ 11 files changed, 34 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index faff6f6dd2..630019d414 100644 --- a/changelog.txt +++ b/changelog.txt @@ -67,6 +67,7 @@ Version 1.08.0 - '-w suffix' or '-w pedantic' command line option enabled 'Suffix ignored' warning for built-in in string functions - __FB_UNIQUEID_PUSH__(), __FB_UNIQUEID__(), __FB_UNIQUEID_POP__(), __FB_ARG_LEFTOF__(), __FB_ARG_RIGHTOF__(), __FB_JOIN__() builtin macros - __FB_ARG_COUNT__() builtin macro +- __thiscall keyword to specify the 'thiscall' calling convention (-gen gcc only) - __FB_QUOTE__(), __FB_UNQUOTE__(), __FB_EVAL__() builtin macros - rtlib: REDIM [PRESERVE] will generate run time error if attempting to resize static (fixed length) arrays. - gas64 emitter for x86_64 (SARG), added '-gen gas64' command line option to select diff --git a/src/compiler/ast-node-call.bas b/src/compiler/ast-node-call.bas index 2487099e5d..163ed954e2 100644 --- a/src/compiler/ast-node-call.bas +++ b/src/compiler/ast-node-call.bas @@ -204,6 +204,7 @@ function astLoadCALL( byval n as ASTNODE ptr ) as IRVREG ptr '' cdecl: pushed arguments must be popped by caller '' pascal/stdcall: callee does it instead + '' thiscall: could be either depending on target !!! TODO !!! argbytes = symbCalcArgLen( l->dtype, l->subtype, arg->arg.mode ) if( symbGetProcMode( proc ) = FB_FUNCMODE_CDECL ) then bytestopop += argbytes diff --git a/src/compiler/fbint.bi b/src/compiler/fbint.bi index 149820e199..9f44d725bd 100644 --- a/src/compiler/fbint.bi +++ b/src/compiler/fbint.bi @@ -346,6 +346,7 @@ enum FB_TOKEN FB_TK_PASCAL FB_TK_CDECL FB_TK_STDCALL + FB_TK_THISCALL FB_TK_ALIAS FB_TK_LIB FB_TK_OVERLOAD diff --git a/src/compiler/ir-hlc.bas b/src/compiler/ir-hlc.bas index cb1ba54ff7..1e07340d36 100644 --- a/src/compiler/ir-hlc.bas +++ b/src/compiler/ir-hlc.bas @@ -555,6 +555,13 @@ private function hEmitProcHeader _ '' Linux GCC only accepts this ln += " __attribute__((stdcall))" end select + case FB_FUNCMODE_THISCALL + select case( env.clopt.target ) + case FB_COMPTARGET_WIN32, FB_COMPTARGET_XBOX + ln += " __thiscall" + case else + ln += " __attribute__((thiscall))" + end select end select end if diff --git a/src/compiler/ir-llvm.bas b/src/compiler/ir-llvm.bas index dcee42f9f3..d8e20a25ba 100644 --- a/src/compiler/ir-llvm.bas +++ b/src/compiler/ir-llvm.bas @@ -367,6 +367,8 @@ private function hEmitProcCallConv( byval proc as FBSYMBOL ptr ) as string select case as const( symbGetProcMode( proc ) ) case FB_FUNCMODE_STDCALL, FB_FUNCMODE_STDCALL_MS, FB_FUNCMODE_PASCAL function = "x86_stdcallcc " + case FB_FUNCMODE_THISCALL + function = "x86_thiscall " end select end function diff --git a/src/compiler/parser-proc.bas b/src/compiler/parser-proc.bas index ec47a82441..6ca899fc73 100644 --- a/src/compiler/parser-proc.bas +++ b/src/compiler/parser-proc.bas @@ -509,7 +509,7 @@ function cProcCallingConv( byval default as FB_FUNCMODE ) as FB_FUNCMODE default = env.target.fbcall end if - '' (CDECL|STDCALL|PASCAL)? + '' (CDECL|STDCALL|PASCAL|THISCALL)? select case as const lexGetToken( ) case FB_TK_CDECL function = FB_FUNCMODE_CDECL @@ -525,6 +525,10 @@ function cProcCallingConv( byval default as FB_FUNCMODE ) as FB_FUNCMODE function = FB_FUNCMODE_PASCAL lexSkipToken( LEXCHECK_POST_SUFFIX ) + case FB_TK_THISCALL + function = FB_FUNCMODE_THISCALL + lexSkipToken( ) + case else select case as const parser.mangling case FB_MANGLING_BASIC, FB_MANGLING_RTLIB diff --git a/src/compiler/symb-keyword.bas b/src/compiler/symb-keyword.bas index 8f629fc168..977fa6dbbb 100644 --- a/src/compiler/symb-keyword.bas +++ b/src/compiler/symb-keyword.bas @@ -108,6 +108,7 @@ dim shared kwdTb( 0 to FB_TOKENS-1 ) as SYMBKWD => _ ( @"FUNCTION" , FB_TK_FUNCTION , FB_TKCLASS_KEYWORD ), _ ( @"CDECL" , FB_TK_CDECL , FB_TKCLASS_KEYWORD ), _ ( @"STDCALL" , FB_TK_STDCALL , FB_TKCLASS_KEYWORD ), _ + ( @"__THISCALL" , FB_TK_THISCALL , FB_TKCLASS_KEYWORD ), _ ( @"PASCAL" , FB_TK_PASCAL , FB_TKCLASS_KEYWORD ), _ ( @"ALIAS" , FB_TK_ALIAS , FB_TKCLASS_KEYWORD ), _ ( @"LIB" , FB_TK_LIB , FB_TKCLASS_KEYWORD ), _ diff --git a/src/compiler/symb-proc.bas b/src/compiler/symb-proc.bas index a0e812c9d5..cca6891411 100644 --- a/src/compiler/symb-proc.bas +++ b/src/compiler/symb-proc.bas @@ -2928,6 +2928,8 @@ private sub hProcModeToStr( byref s as string, byval proc as FBSYMBOL ptr ) select case( symbGetProcMode( proc ) ) case FB_FUNCMODE_STDCALL, FB_FUNCMODE_STDCALL_MS s += " stdcall" + case FB_FUNCMODE_THISCALL + s += " thiscall" case FB_FUNCMODE_PASCAL s += " pascal" end select @@ -2941,6 +2943,8 @@ private sub hProcModeToStr( byref s as string, byval proc as FBSYMBOL ptr ) case else s += " stdcall" end select + case FB_FUNCMODE_THISCALL + s += " thiscall" case FB_FUNCMODE_PASCAL if( env.target.fbcall <> FB_FUNCMODE_PASCAL ) then s += " pascal" diff --git a/src/compiler/symb.bas b/src/compiler/symb.bas index 5d467d3c32..2624ab9191 100644 --- a/src/compiler/symb.bas +++ b/src/compiler/symb.bas @@ -2580,6 +2580,7 @@ function symbDumpToStr _ case FB_FUNCMODE_STDCALL_MS : s += " stdcallms" case FB_FUNCMODE_PASCAL : s += " pascal" case FB_FUNCMODE_CDECL : s += " cdecl" + case FB_FUNCMODE_THISCALL : s += " thiscall" end select if( verbose ) then diff --git a/src/compiler/symb.bi b/src/compiler/symb.bi index 575141d23f..d0b30d3644 100644 --- a/src/compiler/symb.bi +++ b/src/compiler/symb.bi @@ -217,6 +217,7 @@ enum FB_FUNCMODE FB_FUNCMODE_STDCALL_MS '' ms/vb-style: don't include the @n suffix FB_FUNCMODE_CDECL FB_FUNCMODE_PASCAL + FB_FUNCMODE_THISCALL '' Symbolic constant to represent FBCALL, telling cProcCallingConv() '' and the RTL procedure definitions to use env.target.fbcall which diff --git a/todo.txt b/todo.txt index 677c8cd8ed..df2862d8dc 100644 --- a/todo.txt +++ b/todo.txt @@ -68,6 +68,16 @@ o ABI instead of __cdecl as on Linux, even though GCC doesn't use MSVC's name mangling [yet]. - FB can probably support __thiscall with C/LLVM backends pretty easily + [ ] use __thiscall calling by default for mingw gcc 32-bit, currently + currently, users must manage this explicitly, recommend using a + #define thiscall __thiscall depending on target platform + [ ] silently ignore on 64-bit, but warn if declare & procedure is different + [ ] implement in -gen gas 32-bit. Hard to do, AST/IR/EMIT currently doesn't + have enough information or implementation for passing arguments in registers. + [ ] more tests and documentation for mangling/calling conventions, not every + [x] add the __thiscall keyword + [x] add '-z no-thiscall' to ignore "thiscall" and revert to default calling + convention o static member vars - allow STATIC IMPORT, like EXTERN IMPORT (extract common code from cVariableDecl()) From ada09f5073ddda3ca40fc827aeaa3b1ab40da769 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Sat, 6 Jul 2019 06:41:49 -0400 Subject: [PATCH 06/14] fbc: thiscall, add the '-z nothiscall' option - when '-z nothiscall' option is specified, fbc will not use 'thiscall' calling convention. --- src/compiler/fb.bas | 4 ++++ src/compiler/fb.bi | 2 ++ src/compiler/fbc.bas | 2 ++ src/compiler/parser-proc.bas | 5 ++++- 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/compiler/fb.bas b/src/compiler/fb.bas index b1a3675cfd..c65a25575e 100644 --- a/src/compiler/fb.bas +++ b/src/compiler/fb.bas @@ -623,6 +623,8 @@ sub fbSetOption( byval opt as integer, byval value as integer ) env.clopt.gosubsetjmp = value case FB_COMPOPT_VALISTASPTR env.clopt.valistasptr = value + case FB_COMPOPT_NOTHISCALL + env.clopt.nothiscall = value case FB_COMPOPT_EXPORT env.clopt.export = value case FB_COMPOPT_MSBITFIELDS @@ -719,6 +721,8 @@ function fbGetOption( byval opt as integer ) as integer function = env.clopt.gosubsetjmp case FB_COMPOPT_VALISTASPTR function = env.clopt.valistasptr + case FB_COMPOPT_NOTHISCALL + function = env.clopt.valistasptr case FB_COMPOPT_EXPORT function = env.clopt.export case FB_COMPOPT_MSBITFIELDS diff --git a/src/compiler/fb.bi b/src/compiler/fb.bi index a844b1b3ad..e6f8583900 100644 --- a/src/compiler/fb.bi +++ b/src/compiler/fb.bi @@ -96,6 +96,7 @@ enum FB_COMPOPT '' the rest FB_COMPOPT_GOSUBSETJMP '' boolean: implement GOSUB using setjmp/longjump? FB_COMPOPT_VALISTASPTR '' boolean: implement CVA_* using pointer expressions only? + FB_COMPOPT_NOTHISCALL '' boolean: don't use 'thiscall' calling convention? FB_COMPOPT_EXPORT '' boolean: export all symbols declared as EXPORT? FB_COMPOPT_MSBITFIELDS '' boolean: use M$'s bitfields packing? FB_COMPOPT_MULTITHREADED '' boolean: -mt @@ -289,6 +290,7 @@ type FBCMMLINEOPT '' the rest gosubsetjmp as integer '' implement GOSUB using setjmp/longjump? (default = false) valistasptr as integer '' implement CVA_* using pointer expressions only? + nothiscall as integer '' do not use thiscall calling convention (default = false) export as integer '' export all symbols declared as EXPORT (default = true) msbitfields as integer '' use M$'s bitfields packing multithreaded as integer '' link against thread-safe runtime library (default = false) diff --git a/src/compiler/fbc.bas b/src/compiler/fbc.bas index 2754503c25..14bb110159 100644 --- a/src/compiler/fbc.bas +++ b/src/compiler/fbc.bas @@ -2035,6 +2035,8 @@ private sub handleOpt(byval optid as integer, byref arg as string) fbSetOption( FB_COMPOPT_GOSUBSETJMP, TRUE ) case "valist-as-ptr" fbSetOption( FB_COMPOPT_VALISTASPTR, TRUE ) + case "no-thiscall" + fbSetOption( FB_COMPOPT_NOTHISCALL, TRUE ) case else hFatalInvalidOption( arg ) end select diff --git a/src/compiler/parser-proc.bas b/src/compiler/parser-proc.bas index 6ca899fc73..3115bebb2a 100644 --- a/src/compiler/parser-proc.bas +++ b/src/compiler/parser-proc.bas @@ -526,7 +526,10 @@ function cProcCallingConv( byval default as FB_FUNCMODE ) as FB_FUNCMODE lexSkipToken( LEXCHECK_POST_SUFFIX ) case FB_TK_THISCALL - function = FB_FUNCMODE_THISCALL + '' ignore thiscall if '-z no-thiscall' was given + if( env.clopt.nothiscall = FALSE ) then + function = FB_FUNCMODE_THISCALL + end if lexSkipToken( ) case else From 1f16d28c40717229dd317f388767723e4fec5622 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Sun, 30 Jun 2019 12:32:49 -0400 Subject: [PATCH 07/14] fbc: add tests for "this" member procedure parameter - test "__thiscall" calling convention - test "cdecl" calling convetion - test "stdcall" calling convetion --- tests/cpp/this-cpp.cpp | 200 +++++++++++++++++++++++++++++++++++++ tests/cpp/this-fbc.bas | 219 +++++++++++++++++++++++++++++++++++++++++ tests/cpp/this.bmk | 10 ++ 3 files changed, 429 insertions(+) create mode 100644 tests/cpp/this-cpp.cpp create mode 100644 tests/cpp/this-fbc.bas create mode 100644 tests/cpp/this.bmk diff --git a/tests/cpp/this-cpp.cpp b/tests/cpp/this-cpp.cpp new file mode 100644 index 0000000000..b8dafd1d57 --- /dev/null +++ b/tests/cpp/this-cpp.cpp @@ -0,0 +1,200 @@ +static void * ptr1 = 0; +static void * ptr2 = 0; +static void * ptr3 = 0; + + +// global getters: +// we can retrieve argument information passed +// by calling the getters + +void resetChecks() { + ptr1 = ptr2 = ptr3 = 0; +} + +void* getPtr1() { + return ptr1; +} + +void* getPtr2() { + return ptr2; +} + +void* getPtr3() { + return ptr3; +} + +// Default calling convention in c++ + +class UDT_DEFAULT { + public: + int value; + UDT_DEFAULT(); + ~UDT_DEFAULT(); + void loadpointer1 ( void ); + void loadpointer2 ( void* arg ); + void loadpointer3 ( void* arg1, void* arg2 ); +}; + +UDT_DEFAULT::UDT_DEFAULT() { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +UDT_DEFAULT::~UDT_DEFAULT() { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +void UDT_DEFAULT::loadpointer1( void ) { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +void UDT_DEFAULT::loadpointer2( void* arg ) { + ptr1 = this; + ptr2 = arg; + ptr3 = 0; +} + +void UDT_DEFAULT::loadpointer3( void* arg1, void* arg2 ) { + ptr1 = this; + ptr2 = arg1; + ptr3 = arg2; +} + + +// thiscall calling convention in c++ +// Normally, __attribute__((thiscall)) will generate warnings on +// linux x86_64 so we should pass '-Wno-attributes' to gcc/g++ + +class UDT_THISCALL { + public: + int value; + UDT_THISCALL() __attribute__((thiscall)); + ~UDT_THISCALL() __attribute__((thiscall)); + void loadpointer1 ( void ) __attribute__((thiscall)); + void loadpointer2 ( void* arg ) __attribute__((thiscall)); + void loadpointer3 ( void* arg1, void* arg2 ) __attribute__((thiscall)); +}; + +UDT_THISCALL::UDT_THISCALL() { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +UDT_THISCALL::~UDT_THISCALL() { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +void UDT_THISCALL::loadpointer1( void ) { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +void UDT_THISCALL::loadpointer2( void* arg ) { + ptr1 = this; + ptr2 = arg; + ptr3 = 0; +} + +void UDT_THISCALL::loadpointer3( void* arg1, void* arg2 ) { + ptr1 = this; + ptr2 = arg1; + ptr3 = arg2; +} + +// cdecl calling convention in c++ + +class UDT_CDECL { + public: + int value; + UDT_CDECL() __attribute__((cdecl)); + ~UDT_CDECL() __attribute__((cdecl)); + void loadpointer1 ( void ) __attribute__((cdecl)); + void loadpointer2 ( void* arg ) __attribute__((cdecl)); + void loadpointer3 ( void* arg1, void* arg2 ) __attribute__((cdecl)); +}; + +UDT_CDECL::UDT_CDECL() { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +UDT_CDECL::~UDT_CDECL() { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +void UDT_CDECL::loadpointer1( void ) { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +void UDT_CDECL::loadpointer2( void* arg ) { + ptr1 = this; + ptr2 = arg; + ptr3 = 0; +} + +void UDT_CDECL::loadpointer3( void* arg1, void* arg2 ) { + ptr1 = this; + ptr2 = arg1; + ptr3 = arg2; +} + +// stdcall calling convention in c++ + +class UDT_STDCALL { + public: + int value; + UDT_STDCALL() __attribute__((stdcall)); + + // mingw/gcc appears to have a bug that generates wrong + // mangling "__ZN11UDT_STDCALLD1Ev@8" so just use cdecl + // here instead + ~UDT_STDCALL() __attribute__((cdecl)); + + void loadpointer1 ( void ) __attribute__((stdcall)); + void loadpointer2 ( void* arg ) __attribute__((stdcall)); + void loadpointer3 ( void* arg1, void* arg2 ) __attribute__((stdcall)); +}; + +UDT_STDCALL::UDT_STDCALL() { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +UDT_STDCALL::~UDT_STDCALL() { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +void UDT_STDCALL::loadpointer1( void ) { + ptr1 = this; + ptr2 = 0; + ptr3 = 0; +} + +void UDT_STDCALL::loadpointer2( void* arg ) { + ptr1 = this; + ptr2 = arg; + ptr3 = 0; +} + +void UDT_STDCALL::loadpointer3( void* arg1, void* arg2 ) { + ptr1 = this; + ptr2 = arg1; + ptr3 = arg2; +} diff --git a/tests/cpp/this-fbc.bas b/tests/cpp/this-fbc.bas new file mode 100644 index 0000000000..25a04231b9 --- /dev/null +++ b/tests/cpp/this-fbc.bas @@ -0,0 +1,219 @@ +' TEST_MODE : MULTI_MODULE_TEST + +'' test that the instance argument of the caller +'' has same address as the instance parameter +'' in the callee + +#if ENABLE_CHECK_BUGS + #define DOTEST +#else + #if __FB_BACKEND__ <> "gas" + #define DOTEST + #endif +#endif + +#ifdef DOTEST + +extern "c++" + +declare sub resetChecks() +declare function getPtr1() as any ptr +declare function getPtr2() as any ptr +declare function getPtr3() as any ptr + +'' default calling convention +'' on the cpp side, we are not explicitly specifying calling +'' calling convention, and it varies by target/platform + +'' !!! TODO !!! this default should be handled in fbc +#if defined(__FB_WIN32__) and not defined(__FB_64BIT__) + #define thiscall __thiscall +#else + #define thiscall +#endif + +type UDT_DEFAULT + + value as long + + declare constructor thiscall () + declare destructor thiscall () + + declare sub loadpointer1 thiscall () + declare sub loadpointer2 thiscall ( byval arg1 as any ptr ) + declare sub loadpointer3 thiscall ( byval arg1 as any ptr, byval arg2 as any ptr ) + +end type + +'' thiscall calling convention +'' on the cpp side, we are explicitly specifying __atribute__((thiscall)) +'' for all member procs. We do the same here with __thiscall +type UDT_THISCALL + + value as long + + declare constructor __thiscall () + declare destructor __thiscall () + + declare sub loadpointer1 __thiscall () + declare sub loadpointer2 __thiscall ( byval arg1 as any ptr ) + declare sub loadpointer3 __thiscall ( byval arg1 as any ptr, byval arg2 as any ptr ) + +end type + +'' cdecl calling convention +'' on the cpp side, we are explicitly specifying __atribute__((cdecl)) +'' for all member procs. We do the same here with cdecl +type UDT_CDECL + + value as long + + declare constructor cdecl () + declare destructor cdecl () + + declare sub loadpointer1 cdecl () + declare sub loadpointer2 cdecl ( byval arg1 as any ptr ) + declare sub loadpointer3 cdecl ( byval arg1 as any ptr, byval arg2 as any ptr ) + +end type + +'' stdcall calling convention +'' on the cpp side, we are explicitly specifying __atribute__((stdcall)) +'' for all member procs. We do the same here with stdcall +type UDT_STDCALL + + value as long + + declare constructor stdcall () + + '' mingw/gcc appears to have a bug and generates the + '' wrong suffix '@8' for the destructor. On win 32-bit + '' just use cdecl instead + declare destructor cdecl () + + declare sub loadpointer1 stdcall () + declare sub loadpointer2 stdcall ( byval arg1 as any ptr ) + declare sub loadpointer3 stdcall ( byval arg1 as any ptr, byval arg2 as any ptr ) + +end type + +end extern + +#macro checkResults( r1, r2, r3 ) + assert( r1 = getPtr1() ) + assert( r2 = getPtr2() ) + assert( r3 = getPtr3() ) +#endmacro + +'' !!! TODO !!! combine duplicate code to #macro +'' currently separate to track assert locatons + +dim p1 as any ptr = any +dim p2 as any ptr = cast( any ptr, 1111 ) +dim p3 as any ptr = cast( any ptr, 2222 ) + +'' default calling convention +scope + resetChecks() + dim x as UDT_DEFAULT + checkResults( @x, 0, 0 ) + + resetChecks() + x.loadpointer1() + checkResults( @x, 0, 0 ) + + resetChecks() + x.loadpointer2( p2 ) + checkResults( @x, p2, 0 ) + + resetChecks() + x.loadpointer3( p2, p3 ) + checkResults( @x, p2, p3 ) + + resetChecks() + p1 = @x +end scope + +'' check results of destructor called +checkResults( p1, 0, 0 ) + +'' explicit use of thiscall on the cpp side +scope + resetChecks() + dim x as UDT_THISCALL + checkResults( @x, 0, 0 ) + + resetChecks() + x.loadpointer1() + checkResults( @x, 0, 0 ) + + resetChecks() + x.loadpointer2( p2 ) + checkResults( @x, p2, 0 ) + + resetChecks() + x.loadpointer3( p2, p3 ) + checkResults( @x, p2, p3 ) + + resetChecks() + p1 = @x +end scope + +'' check results of destructor called +checkResults( p1, 0, 0 ) + +'' explicit use of thiscall on the cpp side +scope + resetChecks() + dim x as UDT_CDECL + checkResults( @x, 0, 0 ) + + resetChecks() + x.loadpointer1() + checkResults( @x, 0, 0 ) + + resetChecks() + x.loadpointer2( p2 ) + checkResults( @x, p2, 0 ) + + resetChecks() + x.loadpointer3( p2, p3 ) + checkResults( @x, p2, p3 ) + + resetChecks() + p1 = @x +end scope + +'' check results of destructor called +checkResults( p1, 0, 0 ) + +'' explicit use of thiscall on the cpp side +scope + resetChecks() + dim x as UDT_STDCALL + checkResults( @x, 0, 0 ) + + resetChecks() + x.loadpointer1() + checkResults( @x, 0, 0 ) + + resetChecks() + x.loadpointer2( p2 ) + checkResults( @x, p2, 0 ) + + resetChecks() + x.loadpointer3( p2, p3 ) + checkResults( @x, p2, p3 ) + + resetChecks() + p1 = @x +end scope + +'' check results of destructor called +checkResults( p1, 0, 0 ) + +#else + #if ENABLE_CHECK_BUGS + assert( 0 ) + #endif +#endif diff --git a/tests/cpp/this.bmk b/tests/cpp/this.bmk new file mode 100644 index 0000000000..be9341be57 --- /dev/null +++ b/tests/cpp/this.bmk @@ -0,0 +1,10 @@ +# TEST_MODE : MULTI_MODULE_OK + +MAIN := this-fbc.bas +SRCS := + +EXTRA_OBJS := this-cpp.o + +$(SRCDIR)this-cpp.o : $(SRCDIR)this-cpp.cpp + # Pass $(CFLAGS) to get -m32 or -m64 as required + $(CXX) -c $(CFLAGS) -Wall -Wno-attributes -o $@ $^ From 40175d4954d85cb7497d5b443b8b819975cc2394 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Sun, 30 Jun 2019 12:33:09 -0400 Subject: [PATCH 08/14] fbc: add tests for c++ class declaration in fbc --- tests/cpp/class-cpp.cpp | 232 ++++++++++++++++++++++++++++++++++++++++ tests/cpp/class-fbc.bas | 190 ++++++++++++++++++++++++++++++++ tests/cpp/class.bmk | 10 ++ 3 files changed, 432 insertions(+) create mode 100644 tests/cpp/class-cpp.cpp create mode 100644 tests/cpp/class-fbc.bas create mode 100644 tests/cpp/class.bmk diff --git a/tests/cpp/class-cpp.cpp b/tests/cpp/class-cpp.cpp new file mode 100644 index 0000000000..fbd5c42fc2 --- /dev/null +++ b/tests/cpp/class-cpp.cpp @@ -0,0 +1,232 @@ +#define NULL 0 +static const void * ptr1 = NULL; // this/lhs address +static const void * ptr2 = NULL; // rhs address +static char msg1[100]; // check message +static int val1 = 0; // current/lhs value +static int val2 = 0; // rhs value +static int val3 = 0; // result value +static int initial = 1; // 1 = enable ctor/copy-ctor/dtor/let + +void setMsg( const char* msg ) { + char *s = msg1; + while( *msg ) + *s++ = *msg++; + *s = *msg; +} + +void setInitial( int flag ) { + initial = flag; +} + +void resetChecks() { + ptr1 = NULL; + ptr2 = NULL; + setMsg( "" ); + val1 = 0; + val2 = 0; + val3 = 0; +} + +const void* getPtr1() { + return ptr1; +} + +const void* getPtr2() { + return ptr2; +} + +char* getMsg1() { + return msg1; +} + +int getVal1() { + return val1; +} + +int getVal2() { + return val2; +} + +int getVal3() { + return val3; +} + +class UDT +{ + public: + + int value; + void* self; + + UDT(); + UDT( UDT const& rhs ); + UDT( int const& rhs ); + ~UDT(); + + // assignment + UDT& operator=( UDT const& rhs ); + + // !!! TODO !!! non-static member bops + UDT& operator+( int const& rhs ); + UDT& operator-( int const& rhs ); +}; + +UDT operator+( UDT const& lhs, UDT const& rhs ); +UDT operator-( UDT const& lhs, UDT const& rhs ); + +UDT operator+( int const& lhs, UDT const& rhs ); +UDT operator-( int const& lhs, UDT const& rhs ); + +// constructor UDT() +UDT::UDT() +{ + self = this; + if( initial ) { + ptr1 = this; + ptr2 = NULL; + setMsg( "UDT::UDT()" ); + val1 = -1; + val2 = -1; + val3 = 0; + } + value = 0; +} + +// constructor UDT( byref rhs as const UDT ) +UDT::UDT( UDT const& rhs ) +{ + self = this; + if( initial ) { + ptr1 = this; + ptr2 = &rhs; + setMsg( "UDT::UDT( UDT const& rhs )" ); + val1 = -1; + val2 = rhs.value; + val3 = rhs.value; + } + value = rhs.value; +} + +// constructor UDT( byref rhs as const long ) +UDT::UDT( int const& rhs ) +{ + self = this; + if( initial ) { + ptr1 = this; + ptr2 = &rhs; + setMsg( "UDT::UDT( int const& rhs )" ); + val1 = -1; + val2 = rhs; + val3 = rhs; + } + value = rhs; +} + +// destructor UDT() +UDT::~UDT() +{ + if( initial ) { + ptr1 = this; + ptr2 = NULL; + setMsg( "UDT::~UDT()" ); + val1 = value; + val2 = -1; + val3 = -1; + } +} + +// operator UDT.let( byref rhs as const UDT ) +UDT& UDT::operator=( UDT const& rhs ) +{ + if( initial ) { + ptr1 = this; + ptr2 = &rhs; + setMsg( "UDT& UDT::operator=( UDT const& rhs )" ); + val1 = value; + val2 = rhs.value; + val3 = rhs.value; + } + value = rhs.value; + return *this; +} + +// !!! TODO !!!: operator UDT.+( byref rhs as const long ) +UDT& UDT::operator+( int const& rhs ) +{ + ptr1 = this; + ptr2 = &rhs; + setMsg( "UDT& UDT::operator+( int const& rhs )" ); + val1 = value; + val2 = rhs; + val3 = value + rhs; + + value += rhs; + return *this; +} + +// !!! TODO !!!: operator UDT.+( byref rhs as const long ) +UDT& UDT::operator-( int const& rhs ) +{ + ptr1 = this; + ptr2 = &rhs; + setMsg( "UDT& UDT::operator-( int const& rhs )" ); + val1 = value; + val2 = rhs; + val3 = value - rhs; + + value -= rhs; + return *this; +} + +// operator +( byref lhs as const UDT, byref rhs as const UDT ) as UDT +UDT operator+( UDT const& lhs, UDT const& rhs ) +{ + ptr1 = &lhs; + ptr2 = &rhs; + setMsg( "UDT operator+( UDT const& lhs, UDT const& rhs )" ); + val1 = lhs.value; + val2 = rhs.value; + val3 = lhs.value + rhs.value; + + return UDT(lhs.value + rhs.value); +} + +// operator -( byref lhs as const UDT, byref rhs as const UDT ) as UDT +UDT operator-( UDT const& lhs, UDT const& rhs ) +{ + ptr1 = &lhs; + ptr2 = &rhs; + setMsg( "UDT operator-( UDT const& lhs, UDT const& rhs )" ); + val1 = lhs.value; + val2 = rhs.value; + val3 = lhs.value - rhs.value; + + return UDT(lhs.value - rhs.value); +} + +// operator +( byref lhs as const long, byref rhs as const UDT ) as UDT +UDT operator+( int const& lhs, UDT const& rhs ) +{ + ptr1 = &lhs; + ptr2 = &rhs; + setMsg( "UDT operator+( int const& lhs, UDT const& rhs )" ); + val1 = lhs; + val2 = rhs.value; + val3 = lhs + rhs.value; + + return UDT(lhs + rhs.value); +} + +// operator -( byref lhs as const long, byref rhs as const UDT ) as UDT +UDT operator-( int const& lhs, UDT const& rhs ) +{ + ptr1 = &lhs; + ptr2 = &rhs; + setMsg( "UDT operator-( int const& lhs, UDT const& rhs )" ); + val1 = lhs; + val2 = rhs.value; + val3 = lhs - rhs.value; + + return UDT(lhs - rhs.value); +} + diff --git a/tests/cpp/class-fbc.bas b/tests/cpp/class-fbc.bas new file mode 100644 index 0000000000..fd8fbac856 --- /dev/null +++ b/tests/cpp/class-fbc.bas @@ -0,0 +1,190 @@ +' TEST_MODE : MULTI_MODULE_TEST + +'' test mapping of mangling between c++ class and fbc type + +#define DLOG( msg ) '' print #msg + +/' +#undef assert +#macro assert( expr ) + if( (expr) = 0 ) then + '' filename.ext(###): assertion failed at PROC: EXPR + print __FILE__; "("; __LINE__; "): assertion failed at "; __FUNCTION__; ": "; #expr + end if +#endmacro +'/ + +#if ENABLE_CHECK_BUGS + #define DOTEST +#else + #if __FB_BACKEND__ <> "gas" + #define DOTEST + #endif +#endif + +#ifdef DOTEST + +'' !!! TODO !!! this default should be handled in fbc +#if defined(__FB_WIN32__) and not defined(__FB_64BIT__) + #define thiscall __thiscall +#else + #define thiscall +#endif + +#ifndef NULL +#define NULL 0 +#endif + +extern "c++" + +declare sub setInitial( byval flag as long ) +declare sub resetChecks() +declare function getPtr1() as any ptr +declare function getPtr2() as any ptr +declare function getMsg1() as zstring ptr +declare function getVal1() as long +declare function getVal2() as long +declare function getVal3() as long + +type UDT + value as long + self as any ptr + + declare constructor thiscall () + declare constructor thiscall ( byref rhs as const UDT ) + declare constructor thiscall ( byref rhs as const long ) + declare destructor thiscall () + declare operator let thiscall ( byref rhs as const UDT ) + /' declare operator thiscall +( byref rhs as const long ) as UDT '/ + /' declare operator thiscall -( byref rhs as const long ) as UDT '/ + +end type + +'' global operators +declare operator +( byref lhs as const UDT, byref rhs as const UDT ) as UDT +declare operator -( byref lhs as const UDT, byref rhs as const UDT ) as UDT +declare operator +( byref lhs as const long, byref rhs as const UDT ) as UDT +declare operator -( byref lhs as const long, byref rhs as const UDT ) as UDT + +end extern + +#macro checkResults( p1, p2, v1, v2, v3, m1 ) + assert( p1 = getPtr1() ) + assert( p2 = getPtr2() ) + assert( v1 = getVal1() ) + assert( v2 = getVal2() ) + assert( v3 = getVal3() ) + assert( m1 = *getMsg1() ) +#endmacro + +'' enable results for ctor/dtor/copy-ctor/let +setInitial( 1 ) + +DLOG( constructor UDT() ) +scope + resetChecks() + dim a as UDT + checkResults( @a, NULL, -1, -1, 0, "UDT::UDT()" ) + assert( a.value = 0 ) +end scope + +DLOG( destructor UDT() ) +scope + dim p as any ptr + scope + resetChecks() + dim a as UDT + p = @a + checkResults( @a, NULL, -1, -1, 0, "UDT::UDT()" ) + assert( a.value = 0 ) + end scope + checkResults( p, NULL, 0, -1, -1, "UDT::~UDT()" ) +end scope + +DLOG( constructor UDT( byref rhs as const long ) ) +scope + dim a as long = 1 + resetChecks() + dim b as UDT = a + checkResults( @b, @a, -1, 1, 1, "UDT::UDT( int const& rhs )" ) + assert( b.value = 1 ) +end scope + +DLOG( constructor UDT( byref rhs as const UDT ) ) +scope + dim a as UDT = 2 + resetChecks() + dim b as UDT = a + checkResults( @b, @a, -1, 2, 2, "UDT::UDT( UDT const& rhs )" ) + assert( b.value = 2 ) +end scope + +'' disable results for ctor/dtor/copy-ctor/let +setInitial( 0 ) + +/' +'' !!! TODO !!! +DLOG( operator UDT.+( byref rhs as const long ) ) +scope + dim as UDT a = 3 + dim as long b = 11 + dim as UDT c + resetChecks() + c = a + b + checkResults( @a, @b, 3, 11, 14, "UDT::operator+( int rhs )" ) +end scope + +'' !!! TODO !!! +DLOG( operator UDT.-( byref rhs as const long ) as UDT ) +scope + dim as UDT a = 17 + dim as long b = 5 + dim as UDT c + resetChecks() + c = a - b + checkResults( @a, @b, 17, 5, 12, "UDT::operator-( int rhs )" ) +end scope +'/ + +DLOG( operator +( byref lhs as const UDT, byref rhs as const UDT ) as UDT ) +scope + dim as UDT a = 3 + dim as UDT b = 11 + dim as UDT c + resetChecks() + c = a + b + checkResults( @a, @b, 3, 11, 14, "UDT operator+( UDT const& lhs, UDT const& rhs )" ) +end scope + +DLOG( operator -( byref lhs as const UDT, byref rhs as const UDT ) as UDT ) +scope + dim as UDT a = 17 + dim as UDT b = 5 + dim as UDT c + resetChecks() + c = a - b + checkResults( @a, @b, 17, 5, 12, "UDT operator-( UDT const& lhs, UDT const& rhs )" ) +end scope + +DLOG( operator +( byref lhs as const long, byref rhs as const UDT ) as UDT ) +scope + dim as long a = 3 + dim as UDT b = 11 + dim as UDT c + resetChecks() + c = a + b + checkResults( @a, @b, 3, 11, 14, "UDT operator+( int const& lhs, UDT const& rhs )" ) +end scope + +DLOG( operator -( byref lhs as const long, byref rhs as const UDT ) as UDT ) +scope + dim as long a = 17 + dim as UDT b = 5 + dim as UDT c + resetChecks() + c = a - b + checkResults( @a, @b, 17, 5, 12, "UDT operator-( int const& lhs, UDT const& rhs )" ) +end scope + +#endif + diff --git a/tests/cpp/class.bmk b/tests/cpp/class.bmk new file mode 100644 index 0000000000..73f1fd221d --- /dev/null +++ b/tests/cpp/class.bmk @@ -0,0 +1,10 @@ +# TEST_MODE : MULTI_MODULE_OK + +MAIN := class-fbc.bas +SRCS := + +EXTRA_OBJS := class-cpp.o + +$(SRCDIR)class-cpp.o : $(SRCDIR)class-cpp.cpp + # Pass $(CFLAGS) to get -m32 or -m64 as required + $(CXX) -c $(CFLAGS) -o $@ $^ From c2158857440a6851fae13c8d3fa2e2828912c876 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Sun, 7 Jul 2019 11:29:01 -0400 Subject: [PATCH 09/14] fbc: tests/cpp/*.bmk, add warnings and ignore calling convention attributes with '-Wall -Wno-attributes' --- tests/cpp/class.bmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cpp/class.bmk b/tests/cpp/class.bmk index 73f1fd221d..fa3eed4fdf 100644 --- a/tests/cpp/class.bmk +++ b/tests/cpp/class.bmk @@ -7,4 +7,4 @@ EXTRA_OBJS := class-cpp.o $(SRCDIR)class-cpp.o : $(SRCDIR)class-cpp.cpp # Pass $(CFLAGS) to get -m32 or -m64 as required - $(CXX) -c $(CFLAGS) -o $@ $^ + $(CXX) -c $(CFLAGS) -Wall -Wno-attributes -o $@ $^ From f19bfb1965f6743f4514b643be9eda377c49df15 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Sat, 13 Jul 2019 10:20:36 -0400 Subject: [PATCH 10/14] fbc: formatting and comments for calling conventions in tests/cpp --- tests/cpp/class-cpp.cpp | 16 +++++--- tests/cpp/class-fbc.bas | 84 ++++++++++++++++++++--------------------- tests/cpp/this-cpp.cpp | 12 +++--- tests/cpp/this-fbc.bas | 21 ++++++----- 4 files changed, 69 insertions(+), 64 deletions(-) diff --git a/tests/cpp/class-cpp.cpp b/tests/cpp/class-cpp.cpp index fbd5c42fc2..be4afa4d04 100644 --- a/tests/cpp/class-cpp.cpp +++ b/tests/cpp/class-cpp.cpp @@ -1,3 +1,9 @@ +// implementation for class-fbc.bas + +// each operation records call information +// in static variables, and we use getters +// to retrieve it back to fbc side + #define NULL 0 static const void * ptr1 = NULL; // this/lhs address static const void * ptr2 = NULL; // rhs address @@ -51,12 +57,14 @@ int getVal3() { return val3; } +// simple UDT class to test with and +// we declare same on fbc side + class UDT { public: int value; - void* self; UDT(); UDT( UDT const& rhs ); @@ -80,7 +88,6 @@ UDT operator-( int const& lhs, UDT const& rhs ); // constructor UDT() UDT::UDT() { - self = this; if( initial ) { ptr1 = this; ptr2 = NULL; @@ -95,7 +102,6 @@ UDT::UDT() // constructor UDT( byref rhs as const UDT ) UDT::UDT( UDT const& rhs ) { - self = this; if( initial ) { ptr1 = this; ptr2 = &rhs; @@ -110,7 +116,6 @@ UDT::UDT( UDT const& rhs ) // constructor UDT( byref rhs as const long ) UDT::UDT( int const& rhs ) { - self = this; if( initial ) { ptr1 = this; ptr2 = &rhs; @@ -150,6 +155,7 @@ UDT& UDT::operator=( UDT const& rhs ) return *this; } +/* // !!! TODO !!!: operator UDT.+( byref rhs as const long ) UDT& UDT::operator+( int const& rhs ) { @@ -177,6 +183,7 @@ UDT& UDT::operator-( int const& rhs ) value -= rhs; return *this; } +*/ // operator +( byref lhs as const UDT, byref rhs as const UDT ) as UDT UDT operator+( UDT const& lhs, UDT const& rhs ) @@ -229,4 +236,3 @@ UDT operator-( int const& lhs, UDT const& rhs ) return UDT(lhs - rhs.value); } - diff --git a/tests/cpp/class-fbc.bas b/tests/cpp/class-fbc.bas index fd8fbac856..72f7e4d69f 100644 --- a/tests/cpp/class-fbc.bas +++ b/tests/cpp/class-fbc.bas @@ -2,28 +2,18 @@ '' test mapping of mangling between c++ class and fbc type +'' helper macro to track progress #define DLOG( msg ) '' print #msg -/' -#undef assert -#macro assert( expr ) - if( (expr) = 0 ) then - '' filename.ext(###): assertion failed at PROC: EXPR - print __FILE__; "("; __LINE__; "): assertion failed at "; __FUNCTION__; ": "; #expr - end if -#endmacro -'/ - #if ENABLE_CHECK_BUGS #define DOTEST #else + '' thiscall is not supported in -gen gas #if __FB_BACKEND__ <> "gas" #define DOTEST #endif #endif -#ifdef DOTEST - '' !!! TODO !!! this default should be handled in fbc #if defined(__FB_WIN32__) and not defined(__FB_64BIT__) #define thiscall __thiscall @@ -36,47 +26,54 @@ #endif extern "c++" + '' getters to retrieve call information + '' from the c++ side + declare sub setInitial( byval flag as long ) + declare sub resetChecks() + declare function getPtr1() as any ptr + declare function getPtr2() as any ptr + declare function getMsg1() as zstring ptr + declare function getVal1() as long + declare function getVal2() as long + declare function getVal3() as long +end extern + +extern "c++" + '' simple UDT to test with and we + '' declare the same on c++ side + + type UDT + value as long -declare sub setInitial( byval flag as long ) -declare sub resetChecks() -declare function getPtr1() as any ptr -declare function getPtr2() as any ptr -declare function getMsg1() as zstring ptr -declare function getVal1() as long -declare function getVal2() as long -declare function getVal3() as long - -type UDT - value as long - self as any ptr - - declare constructor thiscall () - declare constructor thiscall ( byref rhs as const UDT ) - declare constructor thiscall ( byref rhs as const long ) - declare destructor thiscall () - declare operator let thiscall ( byref rhs as const UDT ) - /' declare operator thiscall +( byref rhs as const long ) as UDT '/ - /' declare operator thiscall -( byref rhs as const long ) as UDT '/ - -end type - -'' global operators -declare operator +( byref lhs as const UDT, byref rhs as const UDT ) as UDT -declare operator -( byref lhs as const UDT, byref rhs as const UDT ) as UDT -declare operator +( byref lhs as const long, byref rhs as const UDT ) as UDT -declare operator -( byref lhs as const long, byref rhs as const UDT ) as UDT + declare constructor thiscall () + declare constructor thiscall ( byref rhs as const UDT ) + declare constructor thiscall ( byref rhs as const long ) + declare destructor thiscall () + declare operator let thiscall ( byref rhs as const UDT ) + /' declare operator thiscall +( byref rhs as const long ) as UDT '/ + /' declare operator thiscall -( byref rhs as const long ) as UDT '/ + + end type + + '' global operators + declare operator +( byref lhs as const UDT, byref rhs as const UDT ) as UDT + declare operator -( byref lhs as const UDT, byref rhs as const UDT ) as UDT + declare operator +( byref lhs as const long, byref rhs as const UDT ) as UDT + declare operator -( byref lhs as const long, byref rhs as const UDT ) as UDT end extern #macro checkResults( p1, p2, v1, v2, v3, m1 ) + assert( m1 = *getMsg1() ) assert( p1 = getPtr1() ) assert( p2 = getPtr2() ) assert( v1 = getVal1() ) assert( v2 = getVal2() ) assert( v3 = getVal3() ) - assert( m1 = *getMsg1() ) #endmacro +#ifdef DOTEST + '' enable results for ctor/dtor/copy-ctor/let setInitial( 1 ) @@ -152,7 +149,7 @@ scope dim as UDT b = 11 dim as UDT c resetChecks() - c = a + b + c = (a + b) checkResults( @a, @b, 3, 11, 14, "UDT operator+( UDT const& lhs, UDT const& rhs )" ) end scope @@ -186,5 +183,4 @@ scope checkResults( @a, @b, 17, 5, 12, "UDT operator-( int const& lhs, UDT const& rhs )" ) end scope -#endif - +#endif '' DOTEST diff --git a/tests/cpp/this-cpp.cpp b/tests/cpp/this-cpp.cpp index b8dafd1d57..5466728a6a 100644 --- a/tests/cpp/this-cpp.cpp +++ b/tests/cpp/this-cpp.cpp @@ -1,12 +1,13 @@ +// implementation for this-fbc.bas + +// each operation records call information +// in static variables, and we use getters +// to retrieve it back to fbc side + static void * ptr1 = 0; static void * ptr2 = 0; static void * ptr3 = 0; - -// global getters: -// we can retrieve argument information passed -// by calling the getters - void resetChecks() { ptr1 = ptr2 = ptr3 = 0; } @@ -69,6 +70,7 @@ void UDT_DEFAULT::loadpointer3( void* arg1, void* arg2 ) { // thiscall calling convention in c++ // Normally, __attribute__((thiscall)) will generate warnings on // linux x86_64 so we should pass '-Wno-attributes' to gcc/g++ +// or later disable emitting the attribute in gcc backend. class UDT_THISCALL { public: diff --git a/tests/cpp/this-fbc.bas b/tests/cpp/this-fbc.bas index 25a04231b9..c1eb9d4ae9 100644 --- a/tests/cpp/this-fbc.bas +++ b/tests/cpp/this-fbc.bas @@ -7,6 +7,7 @@ #if ENABLE_CHECK_BUGS #define DOTEST #else + '' thiscall is not supported in -gen gas #if __FB_BACKEND__ <> "gas" #define DOTEST #endif @@ -15,11 +16,13 @@ #ifdef DOTEST extern "c++" - -declare sub resetChecks() -declare function getPtr1() as any ptr -declare function getPtr2() as any ptr -declare function getPtr3() as any ptr + '' getters to retrieve call information + '' from the c++ side + declare sub resetChecks() + declare function getPtr1() as any ptr + declare function getPtr2() as any ptr + declare function getPtr3() as any ptr +end extern '' default calling convention '' on the cpp side, we are not explicitly specifying calling @@ -32,6 +35,8 @@ declare function getPtr3() as any ptr #define thiscall #endif +extern "c++" + type UDT_DEFAULT value as long @@ -212,8 +217,4 @@ end scope '' check results of destructor called checkResults( p1, 0, 0 ) -#else - #if ENABLE_CHECK_BUGS - assert( 0 ) - #endif -#endif +#endif '' DOTEST From e4efceaf378a971b4f1ee9ecce8506e476b25bba Mon Sep 17 00:00:00 2001 From: coderJeff Date: Sat, 12 Oct 2019 11:04:45 -0400 Subject: [PATCH 11/14] fbc-tests: add test for class assignment operator (let) --- tests/cpp/class-fbc.bas | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/cpp/class-fbc.bas b/tests/cpp/class-fbc.bas index 72f7e4d69f..3bcce86105 100644 --- a/tests/cpp/class-fbc.bas +++ b/tests/cpp/class-fbc.bas @@ -116,6 +116,17 @@ scope assert( b.value = 2 ) end scope +DLOG( operator UDT.let( byref rhs as const UDT ) ) +scope + dim a as UDT = 2 + dim b as UDT + resetChecks() + b = a + checkResults( @b, @a, 0, 2, 2, "UDT& UDT::operator=( UDT const& rhs )" ) + assert( b.value = 2 ) +end scope + + '' disable results for ctor/dtor/copy-ctor/let setInitial( 0 ) From 58790020082a75a94549f1a7f88a9dd69f9a43d4 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Sat, 12 Oct 2019 16:35:31 -0400 Subject: [PATCH 12/14] fbc: thiscall, hidden param for returning structs --- src/compiler/ast-node-call.bas | 2 +- src/compiler/symb-proc.bas | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/ast-node-call.bas b/src/compiler/ast-node-call.bas index 163ed954e2..7b220d75f3 100644 --- a/src/compiler/ast-node-call.bas +++ b/src/compiler/ast-node-call.bas @@ -231,7 +231,7 @@ function astLoadCALL( byval n as ASTNODE ptr ) as IRVREG ptr if( symbProcReturnsOnStack( proc ) ) then '' Pop hidden ptr if cdecl and target doesn't want the callee '' to do it, despite it being cdecl. - if( (symbGetProcMode( proc ) = FB_FUNCMODE_CDECL) and _ + if( ((symbGetProcMode( proc ) = FB_FUNCMODE_CDECL) or (symbGetProcMode( proc ) = FB_FUNCMODE_THISCALL)) and _ ((env.target.options and FB_TARGETOPT_CALLEEPOPSHIDDENPTR) = 0) ) then bytestopop += env.pointersize end if diff --git a/src/compiler/symb-proc.bas b/src/compiler/symb-proc.bas index cca6891411..e6a60d3084 100644 --- a/src/compiler/symb-proc.bas +++ b/src/compiler/symb-proc.bas @@ -160,7 +160,7 @@ end function '' Calculate the number of bytes the procedure needs to pop from stack when returning function symbProcCalcBytesToPop( byval proc as FBSYMBOL ptr ) as longint dim as longint bytestopop = 0 - var notcdecl = (symbGetProcMode( proc ) <> FB_FUNCMODE_CDECL) + var notcdecl = (symbGetProcMode( proc ) <> FB_FUNCMODE_CDECL) and (symbGetProcMode( proc ) <> FB_FUNCMODE_THISCALL) '' Need to pop parameters in case of stdcall/pascal, but not for cdecl if( notcdecl ) then From 686164b2bedc4aa1ae4d820feef33f6fe3d8ce33 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Fri, 30 Apr 2021 10:12:30 -0400 Subject: [PATCH 13/14] fbc: thiscall, add call tests for UDT having simple ctor/dtor --- tests/cpp/call2-cpp.cpp | 370 ++++++++++++++++++++++++++++++++++++++++ tests/cpp/call2-fbc.bas | 186 ++++++++++++++++++++ tests/cpp/call2.bmk | 10 ++ tests/cpp/this-fbc.bas | 4 +- 4 files changed, 568 insertions(+), 2 deletions(-) create mode 100644 tests/cpp/call2-cpp.cpp create mode 100644 tests/cpp/call2-fbc.bas create mode 100644 tests/cpp/call2.bmk diff --git a/tests/cpp/call2-cpp.cpp b/tests/cpp/call2-cpp.cpp new file mode 100644 index 0000000000..b71459340c --- /dev/null +++ b/tests/cpp/call2-cpp.cpp @@ -0,0 +1,370 @@ +// implementation for call2-fbc.bas + +// each operation records call information +// in static variables, and we use getters +// to retrieve it back to fbc side + +#define NULL 0 +static const void * ptr1 = NULL; +static const void * ptr2 = NULL; +static const void * ptr3 = NULL; +static char msg1[100]; // check message + +void setMsg( const char* msg ) { + char *s = msg1; + while( *msg ) + *s++ = *msg++; + *s = *msg; +} + +void resetChecks() { + ptr1 = NULL; + ptr2 = NULL; + ptr3 = NULL; + setMsg( "" ); +} + +const void* getPtr1() { + return ptr1; +} + +const void* getPtr2() { + return ptr2; +} + +const void* getPtr3() { + return ptr3; +} + +char* getMsg1() { + return msg1; +} + +class UDT +{ + public: + int value; + + UDT(); + UDT( int rhs ); + UDT( UDT const& rhs ); + ~UDT(); + +}; + +UDT::UDT() +{ + value = 0; +} + +UDT::UDT( int rhs ) +{ + value = rhs; +} + +UDT::UDT( UDT const& rhs ) +{ + this->value = rhs.value; +} + +UDT::~UDT() +{ +} + +extern "C" { +void sub1_c_default( UDT const& a ); +void sub1_c_cdecl( UDT const& a ) __attribute__((cdecl)); +void sub1_c_stdcall( UDT const& a ) __attribute__((stdcall)); +UDT func1_c_default( UDT const& a ); +UDT func1_c_cdecl( UDT const& a ) __attribute__((cdecl)); +UDT func1_c_stdcall( UDT const& a ) __attribute__((stdcall)); + +void sub2_c_default( UDT const& a, UDT const& b ); +void sub2_c_cdecl( UDT const& a, UDT const& b ) __attribute__((cdecl)); +void sub2_c_stdcall( UDT const& a, UDT const& b ) __attribute__((stdcall)); +UDT func2_c_default( UDT const& a, UDT const& b ); +UDT func2_c_cdecl( UDT const& a, UDT const& b ) __attribute__((cdecl)); +UDT func2_c_stdcall( UDT const& a, UDT const& b ) __attribute__((stdcall)); + +void sub3_c_default( UDT const& a, UDT const& b, UDT const& c ); +void sub3_c_cdecl( UDT const& a, UDT const& b, UDT const& c ) __attribute__((cdecl)); +void sub3_c_stdcall( UDT const& a, UDT const& b, UDT const& c ) __attribute__((stdcall)); +UDT func3_c_default( UDT const& a, UDT const& b, UDT const& c ); +UDT func3_c_cdecl( UDT const& a, UDT const& b, UDT const& c ) __attribute__((cdecl)); +UDT func3_c_stdcall( UDT const& a, UDT const& b, UDT const& c ) __attribute__((stdcall)); +} + +void sub1_cpp_default( UDT const& a ); +void sub1_cpp_cdecl( UDT const& a ) __attribute__((cdecl)); +void sub1_cpp_stdcall( UDT const& a ) __attribute__((stdcall)); +UDT func1_cpp_default( UDT const& a ); +UDT func1_cpp_cdecl( UDT const& a ) __attribute__((cdecl)); +UDT func1_cpp_stdcall( UDT const& a ) __attribute__((stdcall)); + +void sub2_cpp_default( UDT const& a, UDT const& b ); +void sub2_cpp_cdecl( UDT const& a, UDT const& b ) __attribute__((cdecl)); +void sub2_cpp_stdcall( UDT const& a, UDT const& b ) __attribute__((stdcall)); +UDT func2_cpp_default( UDT const& a, UDT const& b ); +UDT func2_cpp_cdecl( UDT const& a, UDT const& b ) __attribute__((cdecl)); +UDT func2_cpp_stdcall( UDT const& a, UDT const& b ) __attribute__((stdcall)); + +void sub3_cpp_default( UDT const& a, UDT const& b, UDT const& c ); +void sub3_cpp_cdecl( UDT const& a, UDT const& b, UDT const& c ) __attribute__((cdecl)); +void sub3_cpp_stdcall( UDT const& a, UDT const& b, UDT const& c ) __attribute__((stdcall)); +UDT func3_cpp_default( UDT const& a, UDT const& b, UDT const& c ); +UDT func3_cpp_cdecl( UDT const& a, UDT const& b, UDT const& c ) __attribute__((cdecl)); +UDT func3_cpp_stdcall( UDT const& a, UDT const& b, UDT const& c ) __attribute__((stdcall)); + +extern "C" { +void sub1_c_default( UDT const& a ) { + ptr1 = &a; + setMsg( "sub1_c_default" ); +} + +void sub1_c_cdecl( UDT const& a ) { + ptr1 = &a; + setMsg( "sub1_c_cdecl" ); +} + +void sub1_c_stdcall( UDT const& a ) { + ptr1 = &a; + setMsg( "sub1_c_stdcall" ); +} + +UDT func1_c_default( UDT const& a ) { + UDT ret = {0}; + ptr1 = &a; + setMsg( "func1_c_default" ); + return ret; +} + +UDT func1_c_cdecl( UDT const& a ) { + UDT ret = {0}; + ptr1 = &a; + setMsg( "func1_c_cdecl" ); + return ret; +} + +UDT func1_c_stdcall( UDT const& a ) { + UDT ret = {0}; + ptr1 = &a; + setMsg( "func1_c_stdcall" ); + return ret; +} + +void sub2_c_default( UDT const& a, UDT const& b ) { + ptr1 = &a; + ptr2 = &b; + setMsg( "sub2_c_default" ); +} + +void sub2_c_cdecl( UDT const& a, UDT const& b ) { + ptr1 = &a; + ptr2 = &b; + setMsg( "sub2_c_cdecl" ); +} + +void sub2_c_stdcall( UDT const& a, UDT const& b ) { + ptr1 = &a; + ptr2 = &b; + setMsg( "sub2_c_stdcall" ); +} + +UDT func2_c_default( UDT const& a, UDT const& b ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_c_default" ); + return ret; +} + +UDT func2_c_cdecl( UDT const& a, UDT const& b ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_c_cdecl" ); + return ret; +} + +UDT func2_c_stdcall( UDT const& a, UDT const& b ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_c_stdcall" ); + return ret; +} + +void sub3_c_default( UDT const& a, UDT const& b, UDT const& c ) { + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "sub3_c_default" ); +} + +void sub3_c_cdecl( UDT const& a, UDT const& b, UDT const& c ) { + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "sub3_c_cdecl" ); +} + +void sub3_c_stdcall( UDT const& a, UDT const& b, UDT const& c ) { + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "sub3_c_stdcall" ); +} + +UDT func3_c_default( UDT const& a, UDT const& b, UDT const& c ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "func3_c_default" ); + return ret; +} + +UDT func3_c_cdecl( UDT const& a, UDT const& b, UDT const& c ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "func3_c_cdecl" ); + return ret; +} + +UDT func3_c_stdcall( UDT const& a, UDT const& b, UDT const& c ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "func3_c_stdcall" ); + return ret; +} +} // extern "C" + +void sub1_cpp_default( UDT const& a ) { + ptr1 = &a; + setMsg( "sub1_cpp_default" ); +} + +void sub1_cpp_cdecl( UDT const& a ) { + ptr1 = &a; + setMsg( "sub1_cpp_cdecl" ); +} + +void sub1_cpp_stdcall( UDT const& a ) { + ptr1 = &a; + setMsg( "sub1_cpp_stdcall" ); +} + +UDT func1_cpp_default( UDT const& a ) { + UDT ret = {0}; + ptr1 = &a; + setMsg( "func1_cpp_default" ); + return ret; +} + +UDT func1_cpp_cdecl( UDT const& a ) { + UDT ret = {0}; + ptr1 = &a; + setMsg( "func1_cpp_cdecl" ); + return ret; +} + +UDT func1_cpp_stdcall( UDT const& a ) { + UDT ret = {0}; + ptr1 = &a; + setMsg( "func1_cpp_stdcall" ); + return ret; +} + +void sub2_cpp_default( UDT const& a, UDT const& b ) { + ptr1 = &a; + ptr2 = &b; + setMsg( "sub2_cpp_default" ); +} + +void sub2_cpp_cdecl( UDT const& a, UDT const& b ) { + ptr1 = &a; + ptr2 = &b; + setMsg( "sub2_cpp_cdecl" ); +} + +void sub2_cpp_stdcall( UDT const& a, UDT const& b ) { + ptr1 = &a; + ptr2 = &b; + setMsg( "sub2_cpp_stdcall" ); +} + +UDT func2_cpp_default( UDT const& a, UDT const& b ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_cpp_default" ); + return ret; +} + +UDT func2_cpp_cdecl( UDT const& a, UDT const& b ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_cpp_cdecl" ); + return ret; +} + +UDT func2_cpp_stdcall( UDT const& a, UDT const& b ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_cpp_stdcall" ); + return ret; +} + +void sub3_cpp_default( UDT const& a, UDT const& b, UDT const& c ) { + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "sub3_cpp_default" ); +} + +void sub3_cpp_cdecl( UDT const& a, UDT const& b, UDT const& c ) { + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "sub3_cpp_cdecl" ); +} + +void sub3_cpp_stdcall( UDT const& a, UDT const& b, UDT const& c ) { + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "sub3_cpp_stdcall" ); +} + +UDT func3_cpp_default( UDT const& a, UDT const& b, UDT const& c ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "func3_cpp_default" ); + return ret; +} + +UDT func3_cpp_cdecl( UDT const& a, UDT const& b, UDT const& c ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "func3_cpp_cdecl" ); + return ret; +} + +UDT func3_cpp_stdcall( UDT const& a, UDT const& b, UDT const& c ) { + UDT ret = {0}; + ptr1 = &a; + ptr2 = &b; + ptr3 = &c; + setMsg( "func3_cpp_stdcall" ); + return ret; +} diff --git a/tests/cpp/call2-fbc.bas b/tests/cpp/call2-fbc.bas new file mode 100644 index 0000000000..d4ad6b31f8 --- /dev/null +++ b/tests/cpp/call2-fbc.bas @@ -0,0 +1,186 @@ +' TEST_MODE : MULTI_MODULE_TEST + +'' test mapping of mangling and calling convention +'' between fbc and c/c++ class procedures + +'' helper macro to track progress +#define DLOG( msg ) '' print #msg + +#if ENABLE_CHECK_BUGS + #define DOTEST +#else + '' thiscall is not supported in -gen gas + #if __FB_BACKEND__ <> "gas" + #define DOTEST + #endif +#endif + +'' !!! TODO !!! this default should be handled in fbc +#if defined(__FB_WIN32__) and not defined(__FB_64BIT__) + #define thiscall __thiscall +#else + #define thiscall +#endif + +#ifndef NULL +#define NULL 0 +#endif + +extern "c++" + '' getters to retrieve call information + '' from the c++ side + declare sub resetChecks() + declare function getPtr1() as any ptr + declare function getPtr2() as any ptr + declare function getPtr3() as any ptr + declare function getMsg1() as zstring ptr + +type UDT + value as long + declare constructor() + declare constructor( byval rhs as long ) + declare constructor( byref rhs as const UDT ) + declare destructor() +end type + +end extern + +extern "c" + declare sub sub1_c_default( byref a as const UDT ) + declare sub sub1_c_cdecl cdecl( byref a as const UDT ) + declare sub sub1_c_stdcall stdcall( byref a as const UDT ) + declare function func1_c_default( byref a as const UDT ) as UDT + declare function func1_c_cdecl cdecl( byref a as const UDT ) as UDT + declare function func1_c_stdcall stdcall( byref a as const UDT ) as UDT + + declare sub sub2_c_default( byref a as const UDT, byref b as const UDT ) + declare sub sub2_c_cdecl cdecl( byref a as const UDT, byref b as const UDT ) + declare sub sub2_c_stdcall stdcall( byref a as const UDT, byref b as const UDT ) + declare function func2_c_default( byref a as const UDT, byref b as const UDT ) as UDT + declare function func2_c_cdecl cdecl( byref a as const UDT, byref b as const UDT ) as UDT + declare function func2_c_stdcall stdcall( byref a as const UDT, byref b as const UDT ) as UDT + + declare sub sub3_c_default( byref a as const UDT, byref b as const UDT, byref c as const UDT ) + declare sub sub3_c_cdecl cdecl( byref a as const UDT, byref b as const UDT, byref c as const UDT ) + declare sub sub3_c_stdcall stdcall( byref a as const UDT, byref b as const UDT, byref c as const UDT ) + declare function func3_c_default( byref a as const UDT, byref b as const UDT, byref c as const UDT ) as UDT + declare function func3_c_cdecl cdecl( byref a as const UDT, byref b as const UDT, byref c as const UDT ) as UDT + declare function func3_c_stdcall stdcall( byref a as const UDT, byref b as const UDT, byref c as const UDT ) as UDT +end extern + +extern "c++" + declare sub sub1_cpp_default( byref a as const UDT ) + declare sub sub1_cpp_cdecl cdecl( byref a as const UDT ) + declare sub sub1_cpp_stdcall stdcall( byref a as const UDT ) + declare function func1_cpp_default( byref a as const UDT ) as UDT + declare function func1_cpp_cdecl cdecl( byref a as const UDT ) as UDT + declare function func1_cpp_stdcall stdcall( byref a as const UDT ) as UDT + + declare sub sub2_cpp_default( byref a as const UDT, byref b as const UDT ) + declare sub sub2_cpp_cdecl cdecl( byref a as const UDT, byref b as const UDT ) + declare sub sub2_cpp_stdcall stdcall( byref a as const UDT, byref b as const UDT ) + declare function func2_cpp_default( byref a as const UDT, byref b as const UDT ) as UDT + declare function func2_cpp_cdecl cdecl( byref a as const UDT, byref b as const UDT ) as UDT + declare function func2_cpp_stdcall stdcall( byref a as const UDT, byref b as const UDT ) as UDT + + declare sub sub3_cpp_default( byref a as const UDT, byref b as const UDT, byref c as const UDT ) + declare sub sub3_cpp_cdecl cdecl( byref a as const UDT, byref b as const UDT, byref c as const UDT ) + declare sub sub3_cpp_stdcall stdcall( byref a as const UDT, byref b as const UDT, byref c as const UDT ) + declare function func3_cpp_default( byref a as const UDT, byref b as const UDT, byref c as const UDT ) as UDT + declare function func3_cpp_cdecl cdecl( byref a as const UDT, byref b as const UDT, byref c as const UDT ) as UDT + declare function func3_cpp_stdcall stdcall( byref a as const UDT, byref b as const UDT, byref c as const UDT ) as UDT +end extern + +#macro chksub( count, n ) + DLOG( n ) + resetChecks() + scope + #if count = 1 + dim a as UDT + n( a ) + assert( @a = getPtr1() ) + #elseif count = 2 + dim a as UDT, b as UDT + n( a, b ) + assert( @a = getPtr1() ) + assert( @b = getPtr2() ) + #else + dim a as UDT, b as UDT, c as UDT + n( a, b, c ) + assert( @a = getPtr1() ) + assert( @b = getPtr2() ) + assert( @c = getPtr3() ) + #endif + assert( #n = *getMsg1() ) + end scope +#endmacro + +#macro chkfunc( count, n ) + DLOG( n ) + resetChecks() + scope + #if count = 1 + dim a as UDT, r as UDT + r = n( a ) + assert( @a = getPtr1() ) + #elseif count = 2 + dim a as UDT, b as UDT, r as UDT + r = n( a, b ) + assert( @a = getPtr1() ) + assert( @b = getPtr2() ) + #else + dim a as UDT, b as UDT, c as UDT, r as UDT + r = n( a, b, c ) + assert( @a = getPtr1() ) + assert( @b = getPtr2() ) + assert( @c = getPtr3() ) + #endif + assert( #n = *getMsg1() ) + end scope +#endmacro + +#ifdef DOTEST + +chksub( 1, sub1_c_default ) +chksub( 1, sub1_c_cdecl ) +chksub( 1, sub1_c_stdcall ) +chkfunc( 1, func1_c_default ) +chkfunc( 1, func1_c_cdecl ) +chkfunc( 1, func1_c_stdcall ) + +chksub( 1, sub1_cpp_default ) +chksub( 1, sub1_cpp_cdecl ) +chksub( 1, sub1_cpp_stdcall ) +chkfunc( 1, func1_cpp_default ) +chkfunc( 1, func1_cpp_cdecl ) +chkfunc( 1, func1_cpp_stdcall ) + +chksub( 2, sub2_c_default ) +chksub( 2, sub2_c_cdecl ) +chksub( 2, sub2_c_stdcall ) +chkfunc( 2, func2_c_default ) +chkfunc( 2, func2_c_cdecl ) +chkfunc( 2, func2_c_stdcall ) + +chksub( 2, sub2_cpp_default ) +chksub( 2, sub2_cpp_cdecl ) +chksub( 2, sub2_cpp_stdcall ) +chkfunc( 2, func2_cpp_default ) +chkfunc( 2, func2_cpp_cdecl ) +chkfunc( 2, func2_cpp_stdcall ) + +chksub( 3, sub3_c_default ) +chksub( 3, sub3_c_cdecl ) +chksub( 3, sub3_c_stdcall ) +chkfunc( 3, func3_c_default ) +chkfunc( 3, func3_c_cdecl ) +chkfunc( 3, func3_c_stdcall ) + +chksub( 3, sub3_cpp_default ) +chksub( 3, sub3_cpp_cdecl ) +chksub( 3, sub3_cpp_stdcall ) +chkfunc( 3, func3_cpp_default ) +chkfunc( 3, func3_cpp_cdecl ) +chkfunc( 3, func3_cpp_stdcall ) + +#endif diff --git a/tests/cpp/call2.bmk b/tests/cpp/call2.bmk new file mode 100644 index 0000000000..55b8574906 --- /dev/null +++ b/tests/cpp/call2.bmk @@ -0,0 +1,10 @@ +# TEST_MODE : MULTI_MODULE_OK + +MAIN := call2-fbc.bas +SRCS := + +EXTRA_OBJS := call2-cpp.o + +$(SRCDIR)call2-cpp.o : $(SRCDIR)call2-cpp.cpp + # Pass $(CFLAGS) to get -m32 or -m64 as required + $(CXX) -c $(CFLAGS) -Wall -Wno-attributes -o $@ $^ diff --git a/tests/cpp/this-fbc.bas b/tests/cpp/this-fbc.bas index c1eb9d4ae9..602e46279e 100644 --- a/tests/cpp/this-fbc.bas +++ b/tests/cpp/this-fbc.bas @@ -13,8 +13,6 @@ #endif #endif -#ifdef DOTEST - extern "c++" '' getters to retrieve call information '' from the c++ side @@ -110,6 +108,8 @@ end extern assert( r3 = getPtr3() ) #endmacro +#ifdef DOTEST + '' !!! TODO !!! combine duplicate code to #macro '' currently separate to track assert locatons From eed449ce256136ff20013aa5a23b7fc79b7a4ec8 Mon Sep 17 00:00:00 2001 From: coderJeff Date: Fri, 30 Apr 2021 11:10:53 -0400 Subject: [PATCH 14/14] fbc: thiscall, fix call2 test for stdc++98 --- tests/cpp/call2-cpp.cpp | 36 ++++++++++++++++++------------------ tests/cpp/call2-fbc.bas | 8 ++++---- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/cpp/call2-cpp.cpp b/tests/cpp/call2-cpp.cpp index b71459340c..52b7d5c38f 100644 --- a/tests/cpp/call2-cpp.cpp +++ b/tests/cpp/call2-cpp.cpp @@ -132,21 +132,21 @@ void sub1_c_stdcall( UDT const& a ) { } UDT func1_c_default( UDT const& a ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; setMsg( "func1_c_default" ); return ret; } UDT func1_c_cdecl( UDT const& a ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; setMsg( "func1_c_cdecl" ); return ret; } UDT func1_c_stdcall( UDT const& a ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; setMsg( "func1_c_stdcall" ); return ret; @@ -171,7 +171,7 @@ void sub2_c_stdcall( UDT const& a, UDT const& b ) { } UDT func2_c_default( UDT const& a, UDT const& b ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; setMsg( "func2_c_default" ); @@ -179,7 +179,7 @@ UDT func2_c_default( UDT const& a, UDT const& b ) { } UDT func2_c_cdecl( UDT const& a, UDT const& b ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; setMsg( "func2_c_cdecl" ); @@ -187,7 +187,7 @@ UDT func2_c_cdecl( UDT const& a, UDT const& b ) { } UDT func2_c_stdcall( UDT const& a, UDT const& b ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; setMsg( "func2_c_stdcall" ); @@ -216,7 +216,7 @@ void sub3_c_stdcall( UDT const& a, UDT const& b, UDT const& c ) { } UDT func3_c_default( UDT const& a, UDT const& b, UDT const& c ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; ptr3 = &c; @@ -225,7 +225,7 @@ UDT func3_c_default( UDT const& a, UDT const& b, UDT const& c ) { } UDT func3_c_cdecl( UDT const& a, UDT const& b, UDT const& c ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; ptr3 = &c; @@ -234,7 +234,7 @@ UDT func3_c_cdecl( UDT const& a, UDT const& b, UDT const& c ) { } UDT func3_c_stdcall( UDT const& a, UDT const& b, UDT const& c ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; ptr3 = &c; @@ -259,21 +259,21 @@ void sub1_cpp_stdcall( UDT const& a ) { } UDT func1_cpp_default( UDT const& a ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; setMsg( "func1_cpp_default" ); return ret; } UDT func1_cpp_cdecl( UDT const& a ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; setMsg( "func1_cpp_cdecl" ); return ret; } UDT func1_cpp_stdcall( UDT const& a ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; setMsg( "func1_cpp_stdcall" ); return ret; @@ -298,7 +298,7 @@ void sub2_cpp_stdcall( UDT const& a, UDT const& b ) { } UDT func2_cpp_default( UDT const& a, UDT const& b ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; setMsg( "func2_cpp_default" ); @@ -306,7 +306,7 @@ UDT func2_cpp_default( UDT const& a, UDT const& b ) { } UDT func2_cpp_cdecl( UDT const& a, UDT const& b ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; setMsg( "func2_cpp_cdecl" ); @@ -314,7 +314,7 @@ UDT func2_cpp_cdecl( UDT const& a, UDT const& b ) { } UDT func2_cpp_stdcall( UDT const& a, UDT const& b ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; setMsg( "func2_cpp_stdcall" ); @@ -343,7 +343,7 @@ void sub3_cpp_stdcall( UDT const& a, UDT const& b, UDT const& c ) { } UDT func3_cpp_default( UDT const& a, UDT const& b, UDT const& c ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; ptr3 = &c; @@ -352,7 +352,7 @@ UDT func3_cpp_default( UDT const& a, UDT const& b, UDT const& c ) { } UDT func3_cpp_cdecl( UDT const& a, UDT const& b, UDT const& c ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; ptr3 = &c; @@ -361,7 +361,7 @@ UDT func3_cpp_cdecl( UDT const& a, UDT const& b, UDT const& c ) { } UDT func3_cpp_stdcall( UDT const& a, UDT const& b, UDT const& c ) { - UDT ret = {0}; + UDT ret = UDT(0); ptr1 = &a; ptr2 = &b; ptr3 = &c; diff --git a/tests/cpp/call2-fbc.bas b/tests/cpp/call2-fbc.bas index d4ad6b31f8..336ceff551 100644 --- a/tests/cpp/call2-fbc.bas +++ b/tests/cpp/call2-fbc.bas @@ -37,10 +37,10 @@ extern "c++" type UDT value as long - declare constructor() - declare constructor( byval rhs as long ) - declare constructor( byref rhs as const UDT ) - declare destructor() + declare constructor thiscall () + declare constructor thiscall ( byval rhs as long ) + declare constructor thiscall ( byref rhs as const UDT ) + declare destructor thiscall () end type end extern