diff --git a/changelog.txt b/changelog.txt index 8c11fa09e7..630019d414 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 @@ -66,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-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-call.bas b/src/compiler/ast-node-call.bas index 2487099e5d..7b220d75f3 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 @@ -230,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/ast-node-proc.bas b/src/compiler/ast-node-proc.bas index 9c2b9c974d..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( ) @@ -500,7 +501,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( (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 @@ -672,13 +673,18 @@ function astProcEnd( byval callrtexit as integer ) as integer res = (symbCheckLabels(symbGetProcSymbTbHead(parser.currproc)) = 0) if( res ) then - '' Destructor? - if( symbIsDestructor( sym ) and enable_implicit_code ) then + '' 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, _ @@ -1296,7 +1322,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/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/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/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-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-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 c5a8a849ac..3115bebb2a 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,13 @@ function cProcCallingConv( byval default as FB_FUNCMODE ) as FB_FUNCMODE function = FB_FUNCMODE_PASCAL lexSkipToken( LEXCHECK_POST_SUFFIX ) + case FB_TK_THISCALL + '' ignore thiscall if '-z no-thiscall' was given + if( env.clopt.nothiscall = FALSE ) then + function = FB_FUNCMODE_THISCALL + end if + lexSkipToken( ) + case else select case as const parser.mangling case FB_MANGLING_BASIC, FB_MANGLING_RTLIB @@ -1056,7 +1063,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 @@ -1464,6 +1473,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 @@ -1554,7 +1578,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 @@ -1762,7 +1786,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/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 924bf33621..745f3a3e33 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 @@ -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 f953de7180..8d7a845424 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,29 @@ sub symbUdtDeclareDefaultMembers _ symbUdtAllocExt( udt ) '' 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 ) + 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 ) + + 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.dtor, TRUE ) + 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 @@ -685,8 +703,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 +755,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 +805,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->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( symbIsDestructor( proc ) ) + assert( symbIsDestructor0( proc ) ) symbUdtAllocExt( sym ) - if( sym->udt.ext->dtor = NULL ) then + 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 +854,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-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-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..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 @@ -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 ) @@ -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 @@ -742,13 +744,19 @@ private function hSetupProc _ '' 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? - 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,10 +928,10 @@ 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 ) + 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) @@ -1601,7 +1609,7 @@ function symbFindCtorProc _ ) as FBSYMBOL ptr '' dtor? can't overload.. - if( symbIsDestructor( 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 ) @@ -2845,7 +2853,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,10 +2924,12 @@ 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" + case FB_FUNCMODE_THISCALL + s += " thiscall" case FB_FUNCMODE_PASCAL s += " pascal" end select @@ -2933,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" @@ -3045,7 +3057,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-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.bas b/src/compiler/symb.bas index 751a0cee2f..2624ab9191 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 ) @@ -2579,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 7aa6d5dde2..d0b30d3644 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 - complete destructor 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 @@ -216,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 @@ -485,18 +487,19 @@ 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 + 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 - 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 @@ -916,11 +919,12 @@ 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 + dtor1 as FBSYMBOL ptr + dtor0 as FBSYMBOL ptr + copyctor as FBSYMBOL ptr + copyctorconst as FBSYMBOL ptr + copyletopconst as FBSYMBOL ptr end type #include once "ast.bi" @@ -1884,10 +1888,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 @@ -2498,7 +2504,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) diff --git a/tests/cpp/call2-cpp.cpp b/tests/cpp/call2-cpp.cpp new file mode 100644 index 0000000000..52b7d5c38f --- /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 = UDT(0); + ptr1 = &a; + setMsg( "func1_c_default" ); + return ret; +} + +UDT func1_c_cdecl( UDT const& a ) { + UDT ret = UDT(0); + ptr1 = &a; + setMsg( "func1_c_cdecl" ); + return ret; +} + +UDT func1_c_stdcall( UDT const& a ) { + UDT ret = UDT(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 = UDT(0); + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_c_default" ); + return ret; +} + +UDT func2_c_cdecl( UDT const& a, UDT const& b ) { + UDT ret = UDT(0); + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_c_cdecl" ); + return ret; +} + +UDT func2_c_stdcall( UDT const& a, UDT const& b ) { + UDT ret = UDT(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 = UDT(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 = UDT(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 = UDT(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 = UDT(0); + ptr1 = &a; + setMsg( "func1_cpp_default" ); + return ret; +} + +UDT func1_cpp_cdecl( UDT const& a ) { + UDT ret = UDT(0); + ptr1 = &a; + setMsg( "func1_cpp_cdecl" ); + return ret; +} + +UDT func1_cpp_stdcall( UDT const& a ) { + UDT ret = UDT(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 = UDT(0); + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_cpp_default" ); + return ret; +} + +UDT func2_cpp_cdecl( UDT const& a, UDT const& b ) { + UDT ret = UDT(0); + ptr1 = &a; + ptr2 = &b; + setMsg( "func2_cpp_cdecl" ); + return ret; +} + +UDT func2_cpp_stdcall( UDT const& a, UDT const& b ) { + UDT ret = UDT(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 = UDT(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 = UDT(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 = UDT(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..336ceff551 --- /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 thiscall () + declare constructor thiscall ( byval rhs as long ) + declare constructor thiscall ( byref rhs as const UDT ) + declare destructor thiscall () +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/class-cpp.cpp b/tests/cpp/class-cpp.cpp new file mode 100644 index 0000000000..be4afa4d04 --- /dev/null +++ b/tests/cpp/class-cpp.cpp @@ -0,0 +1,238 @@ +// 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 +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; +} + +// simple UDT class to test with and +// we declare same on fbc side + +class UDT +{ + public: + + int value; + + 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() +{ + 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 ) +{ + 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 ) +{ + 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..3bcce86105 --- /dev/null +++ b/tests/cpp/class-fbc.bas @@ -0,0 +1,197 @@ +' TEST_MODE : MULTI_MODULE_TEST + +'' test mapping of mangling between c++ class and fbc type + +'' 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 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 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() ) +#endmacro + +#ifdef DOTEST + +'' 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 + +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 ) + +/' +'' !!! 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 '' DOTEST diff --git a/tests/cpp/class.bmk b/tests/cpp/class.bmk new file mode 100644 index 0000000000..fa3eed4fdf --- /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) -Wall -Wno-attributes -o $@ $^ diff --git a/tests/cpp/this-cpp.cpp b/tests/cpp/this-cpp.cpp new file mode 100644 index 0000000000..5466728a6a --- /dev/null +++ b/tests/cpp/this-cpp.cpp @@ -0,0 +1,202 @@ +// 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; + +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++ +// or later disable emitting the attribute in gcc backend. + +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..602e46279e --- /dev/null +++ b/tests/cpp/this-fbc.bas @@ -0,0 +1,220 @@ +' 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 + '' thiscall is not supported in -gen gas + #if __FB_BACKEND__ <> "gas" + #define DOTEST + #endif +#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 +end extern + +'' 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 + +extern "c++" + +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 + +#ifdef DOTEST + +'' !!! 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 ) + +#endif '' DOTEST 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 $@ $^ 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())