#include // for find, all_of #include // for assertf, assert, strict_dynamic_cast #include // for ostream, cerr, endl #include // for back_insert_iterator, back_inserter #include // for __shared_ptr #include "Common/SemanticError.h" // for SemanticError #include "Common/UniqueName.h" // for UniqueName #include "Common/utility.h" // for toString, deleteAll, maybeClone #include "GenPoly/GenPoly.h" // for getFunctionType #include "InitTweak.h" #include "Parser/LinkageSpec.h" // for Spec, isBuiltin, Intrinsic #include "ResolvExpr/typeops.h" // for typesCompatibleIgnoreQualifiers #include "SymTab/Autogen.h" #include "SymTab/Indexer.h" // for Indexer #include "SynTree/Attribute.h" // for Attribute #include "SynTree/Constant.h" // for Constant #include "SynTree/Declaration.h" // for ObjectDecl, DeclarationWithType #include "SynTree/Expression.h" // for Expression, UntypedExpr, Applicati... #include "SynTree/Initializer.h" // for Initializer, ListInit, Designation #include "SynTree/Label.h" // for Label #include "SynTree/Statement.h" // for CompoundStmt, ExprStmt, BranchStmt #include "SynTree/Type.h" // for FunctionType, ArrayType, PointerType #include "SynTree/Visitor.h" // for Visitor, maybeAccept #include "Tuples/Tuples.h" // for Tuples::isTtype class UntypedValofExpr; namespace InitTweak { namespace { class HasDesignations : public Visitor { public: bool hasDesignations = false; virtual void visit( Designation * des ) { if ( ! des->get_designators().empty() ) hasDesignations = true; else Visitor::visit( des ); } }; class InitDepthChecker : public Visitor { public: bool depthOkay = true; Type * type; int curDepth = 0, maxDepth = 0; InitDepthChecker( Type * type ) : type( type ) { Type * t = type; while ( ArrayType * at = dynamic_cast< ArrayType * >( t ) ) { maxDepth++; t = at->get_base(); } maxDepth++; } virtual void visit( ListInit * listInit ) { curDepth++; if ( curDepth > maxDepth ) depthOkay = false; Visitor::visit( listInit ); curDepth--; } }; class InitFlattener : public Visitor { public: virtual void visit( SingleInit * singleInit ); virtual void visit( ListInit * listInit ); std::list< Expression * > argList; }; void InitFlattener::visit( SingleInit * singleInit ) { argList.push_back( singleInit->get_value()->clone() ); } void InitFlattener::visit( ListInit * listInit ) { // flatten nested list inits std::list::iterator it = listInit->begin(); for ( ; it != listInit->end(); ++it ) { (*it)->accept( *this ); } } } std::list< Expression * > makeInitList( Initializer * init ) { InitFlattener flattener; maybeAccept( init, flattener ); return flattener.argList; } bool isDesignated( Initializer * init ) { HasDesignations finder; maybeAccept( init, finder ); return finder.hasDesignations; } bool checkInitDepth( ObjectDecl * objDecl ) { InitDepthChecker checker( objDecl->get_type() ); maybeAccept( objDecl->get_init(), checker ); return checker.depthOkay; } class InitExpander::ExpanderImpl { public: virtual ~ExpanderImpl() = default; virtual std::list< Expression * > next( std::list< Expression * > & indices ) = 0; virtual Statement * buildListInit( UntypedExpr * callExpr, std::list< Expression * > & indices ) = 0; }; class InitImpl : public InitExpander::ExpanderImpl { public: InitImpl( Initializer * init ) : init( init ) {} virtual ~InitImpl() = default; virtual std::list< Expression * > next( __attribute((unused)) std::list< Expression * > & indices ) { // this is wrong, but just a placeholder for now // if ( ! flattened ) flatten( indices ); // return ! inits.empty() ? makeInitList( inits.front() ) : std::list< Expression * >(); return makeInitList( init ); } virtual Statement * buildListInit( UntypedExpr * callExpr, std::list< Expression * > & indices ); private: Initializer * init; }; class ExprImpl : public InitExpander::ExpanderImpl { public: ExprImpl( Expression * expr ) : arg( expr ) {} virtual ~ExprImpl() { delete arg; } virtual std::list< Expression * > next( std::list< Expression * > & indices ) { std::list< Expression * > ret; Expression * expr = maybeClone( arg ); if ( expr ) { for ( std::list< Expression * >::reverse_iterator it = indices.rbegin(); it != indices.rend(); ++it ) { // go through indices and layer on subscript exprs ?[?] ++it; UntypedExpr * subscriptExpr = new UntypedExpr( new NameExpr( "?[?]") ); subscriptExpr->get_args().push_back( expr ); subscriptExpr->get_args().push_back( (*it)->clone() ); expr = subscriptExpr; } ret.push_back( expr ); } return ret; } virtual Statement * buildListInit( UntypedExpr * callExpr, std::list< Expression * > & indices ); private: Expression * arg; }; InitExpander::InitExpander( Initializer * init ) : expander( new InitImpl( init ) ) {} InitExpander::InitExpander( Expression * expr ) : expander( new ExprImpl( expr ) ) {} std::list< Expression * > InitExpander::operator*() { return cur; } InitExpander & InitExpander::operator++() { cur = expander->next( indices ); return *this; } // use array indices list to build switch statement void InitExpander::addArrayIndex( Expression * index, Expression * dimension ) { indices.push_back( index ); indices.push_back( dimension ); } void InitExpander::clearArrayIndices() { deleteAll( indices ); indices.clear(); } bool InitExpander::addReference() { bool added = false; for ( Expression *& expr : cur ) { expr = new AddressExpr( expr ); added = true; } return added; } namespace { /// given index i, dimension d, initializer init, and callExpr f, generates /// if (i < d) f(..., init) /// ++i; /// so that only elements within the range of the array are constructed template< typename OutIterator > void buildCallExpr( UntypedExpr * callExpr, Expression * index, Expression * dimension, Initializer * init, OutIterator out ) { UntypedExpr * cond = new UntypedExpr( new NameExpr( "?get_args().push_back( index->clone() ); cond->get_args().push_back( dimension->clone() ); std::list< Expression * > args = makeInitList( init ); callExpr->get_args().splice( callExpr->get_args().end(), args ); *out++ = new IfStmt( cond, new ExprStmt( callExpr ), nullptr ); UntypedExpr * increment = new UntypedExpr( new NameExpr( "++?" ) ); increment->get_args().push_back( index->clone() ); *out++ = new ExprStmt( increment ); } template< typename OutIterator > void build( UntypedExpr * callExpr, InitExpander::IndexList::iterator idx, InitExpander::IndexList::iterator idxEnd, Initializer * init, OutIterator out ) { if ( idx == idxEnd ) return; Expression * index = *idx++; assert( idx != idxEnd ); Expression * dimension = *idx++; // xxx - may want to eventually issue a warning here if we can detect // that the number of elements exceeds to dimension of the array if ( idx == idxEnd ) { if ( ListInit * listInit = dynamic_cast< ListInit * >( init ) ) { for ( Initializer * init : *listInit ) { buildCallExpr( callExpr->clone(), index, dimension, init, out ); } } else { buildCallExpr( callExpr->clone(), index, dimension, init, out ); } } else { std::list< Statement * > branches; unsigned long cond = 0; ListInit * listInit = dynamic_cast< ListInit * >( init ); if ( ! listInit ) { // xxx - this shouldn't be an error, but need a way to // terminate without creating output, so should catch this error throw SemanticError( "unbalanced list initializers" ); } static UniqueName targetLabel( "L__autogen__" ); Label switchLabel( targetLabel.newName(), 0, std::list< Attribute * >{ new Attribute("unused") } ); for ( Initializer * init : *listInit ) { Expression * condition; // check for designations // if ( init-> ) { condition = new ConstantExpr( Constant::from_ulong( cond ) ); ++cond; // } else { // condition = // ... take designation // cond = // ... take designation+1 // } std::list< Statement * > stmts; build( callExpr, idx, idxEnd, init, back_inserter( stmts ) ); stmts.push_back( new BranchStmt( switchLabel, BranchStmt::Break ) ); CaseStmt * caseStmt = new CaseStmt( condition, stmts ); branches.push_back( caseStmt ); } *out++ = new SwitchStmt( index->clone(), branches ); *out++ = new NullStmt( { switchLabel } ); } } } // if array came with an initializer list: initialize each element // may have more initializers than elements in the array - need to check at each index that // we haven't exceeded size. // may have fewer initializers than elements in the array - need to default construct // remaining elements. // To accomplish this, generate switch statement, consuming all of expander's elements Statement * InitImpl::buildListInit( UntypedExpr * dst, std::list< Expression * > & indices ) { if ( ! init ) return nullptr; CompoundStmt * block = new CompoundStmt(); build( dst, indices.begin(), indices.end(), init, back_inserter( block->get_kids() ) ); if ( block->get_kids().empty() ) { delete block; return nullptr; } else { init = nullptr; // init was consumed in creating the list init return block; } } Statement * ExprImpl::buildListInit( UntypedExpr *, std::list< Expression * > & ) { return nullptr; } Statement * InitExpander::buildListInit( UntypedExpr * dst ) { return expander->buildListInit( dst, indices ); } Type * getTypeofThis( FunctionType * ftype ) { assertf( ftype, "getTypeofThis: nullptr ftype" ); ObjectDecl * thisParam = getParamThis( ftype ); ReferenceType * refType = strict_dynamic_cast< ReferenceType * >( thisParam->type ); return refType->base; } ObjectDecl * getParamThis( FunctionType * ftype ) { assertf( ftype, "getParamThis: nullptr ftype" ); auto & params = ftype->parameters; assertf( ! params.empty(), "getParamThis: ftype with 0 parameters: %s", toString( ftype ).c_str() ); return strict_dynamic_cast< ObjectDecl * >( params.front() ); } bool tryConstruct( DeclarationWithType * dwt ) { ObjectDecl * objDecl = dynamic_cast< ObjectDecl * >( dwt ); if ( ! objDecl ) return false; return ! LinkageSpec::isBuiltin( objDecl->get_linkage() ) && (objDecl->get_init() == nullptr || ( objDecl->get_init() != nullptr && objDecl->get_init()->get_maybeConstructed() )) && ! objDecl->get_storageClasses().is_extern && isConstructable( objDecl->type ); } bool isConstructable( Type * type ) { return ! dynamic_cast< VarArgsType * >( type ) && ! dynamic_cast< ReferenceType * >( type ) && ! dynamic_cast< FunctionType * >( type ) && ! Tuples::isTtype( type ); } class CallFinder : public Visitor { public: typedef Visitor Parent; CallFinder( const std::list< std::string > & names ) : names( names ) {} virtual void visit( ApplicationExpr * appExpr ) { handleCallExpr( appExpr ); } virtual void visit( UntypedExpr * untypedExpr ) { handleCallExpr( untypedExpr ); } std::list< Expression * > * matches; private: const std::list< std::string > names; template< typename CallExpr > void handleCallExpr( CallExpr * expr ) { Parent::visit( expr ); std::string fname = getFunctionName( expr ); if ( std::find( names.begin(), names.end(), fname ) != names.end() ) { matches->push_back( expr ); } } }; void collectCtorDtorCalls( Statement * stmt, std::list< Expression * > & matches ) { static CallFinder finder( std::list< std::string >{ "?{}", "^?{}" } ); finder.matches = &matches; maybeAccept( stmt, finder ); } Expression * getCtorDtorCall( Statement * stmt ) { std::list< Expression * > matches; collectCtorDtorCalls( stmt, matches ); assert( matches.size() <= 1 ); return matches.size() == 1 ? matches.front() : nullptr; } namespace { DeclarationWithType * getCalledFunction( Expression * expr ); template DeclarationWithType * handleDerefCalledFunction( CallExpr * expr ) { // (*f)(x) => should get "f" std::string name = getFunctionName( expr ); assertf( name == "*?", "Unexpected untyped expression: %s", name.c_str() ); assertf( ! expr->get_args().empty(), "Cannot get called function from dereference with no arguments" ); return getCalledFunction( expr->get_args().front() ); } DeclarationWithType * getCalledFunction( Expression * expr ) { assert( expr ); if ( VariableExpr * varExpr = dynamic_cast< VariableExpr * >( expr ) ) { return varExpr->var; } else if ( MemberExpr * memberExpr = dynamic_cast< MemberExpr * >( expr ) ) { return memberExpr->member; } else if ( CastExpr * castExpr = dynamic_cast< CastExpr * >( expr ) ) { return getCalledFunction( castExpr->arg ); } else if ( UntypedExpr * untypedExpr = dynamic_cast< UntypedExpr * >( expr ) ) { return handleDerefCalledFunction( untypedExpr ); } else if ( ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * > ( expr ) ) { return handleDerefCalledFunction( appExpr ); } else if ( AddressExpr * addrExpr = dynamic_cast< AddressExpr * >( expr ) ) { return getCalledFunction( addrExpr->arg ); } else if ( CommaExpr * commaExpr = dynamic_cast< CommaExpr * >( expr ) ) { return getCalledFunction( commaExpr->arg2 ); } return nullptr; } } DeclarationWithType * getFunction( Expression * expr ) { if ( ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * >( expr ) ) { return getCalledFunction( appExpr->get_function() ); } else if ( UntypedExpr * untyped = dynamic_cast< UntypedExpr * > ( expr ) ) { return getCalledFunction( untyped->get_function() ); } assertf( false, "getFunction received unknown expression: %s", toString( expr ).c_str() ); } ApplicationExpr * isIntrinsicCallExpr( Expression * expr ) { ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * >( expr ); if ( ! appExpr ) return nullptr; DeclarationWithType * function = getCalledFunction( appExpr->get_function() ); assertf( function, "getCalledFunction returned nullptr: %s", toString( appExpr->get_function() ).c_str() ); // check for Intrinsic only - don't want to remove all overridable ctor/dtors because autogenerated ctor/dtor // will call all member dtors, and some members may have a user defined dtor. return function->get_linkage() == LinkageSpec::Intrinsic ? appExpr : nullptr; } namespace { template bool allofCtorDtor( Statement * stmt, const Predicate & pred ) { std::list< Expression * > callExprs; collectCtorDtorCalls( stmt, callExprs ); // if ( callExprs.empty() ) return false; // xxx - do I still need this check? return std::all_of( callExprs.begin(), callExprs.end(), pred); } } bool isIntrinsicSingleArgCallStmt( Statement * stmt ) { return allofCtorDtor( stmt, []( Expression * callExpr ){ if ( ApplicationExpr * appExpr = isIntrinsicCallExpr( callExpr ) ) { FunctionType *funcType = GenPoly::getFunctionType( appExpr->get_function()->get_result() ); assert( funcType ); return funcType->get_parameters().size() == 1; } return false; }); } bool isIntrinsicCallStmt( Statement * stmt ) { return allofCtorDtor( stmt, []( Expression * callExpr ) { return isIntrinsicCallExpr( callExpr ); }); } namespace { template Expression *& callArg( CallExpr * callExpr, unsigned int pos ) { if ( pos >= callExpr->get_args().size() ) assertf( false, "getCallArg for argument that doesn't exist: (%u); %s.", pos, toString( callExpr ).c_str() ); for ( Expression *& arg : callExpr->get_args() ) { if ( pos == 0 ) return arg; pos--; } assert( false ); } } Expression *& getCallArg( Expression * callExpr, unsigned int pos ) { if ( ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * >( callExpr ) ) { return callArg( appExpr, pos ); } else if ( UntypedExpr * untypedExpr = dynamic_cast< UntypedExpr * >( callExpr ) ) { return callArg( untypedExpr, pos ); } else if ( TupleAssignExpr * tupleExpr = dynamic_cast< TupleAssignExpr * > ( callExpr ) ) { std::list< Statement * > & stmts = tupleExpr->get_stmtExpr()->get_statements()->get_kids(); assertf( ! stmts.empty(), "TupleAssignExpr somehow has no statements." ); ExprStmt * stmt = strict_dynamic_cast< ExprStmt * >( stmts.back() ); TupleExpr * tuple = strict_dynamic_cast< TupleExpr * >( stmt->get_expr() ); assertf( ! tuple->get_exprs().empty(), "TupleAssignExpr somehow has empty tuple expr." ); return getCallArg( tuple->get_exprs().front(), pos ); } else if ( ImplicitCopyCtorExpr * copyCtor = dynamic_cast< ImplicitCopyCtorExpr * >( callExpr ) ) { return getCallArg( copyCtor->callExpr, pos ); } else { assertf( false, "Unexpected expression type passed to getCallArg: %s", toString( callExpr ).c_str() ); } } namespace { std::string funcName( Expression * func ); template std::string handleDerefName( CallExpr * expr ) { // (*f)(x) => should get name "f" std::string name = getFunctionName( expr ); assertf( name == "*?", "Unexpected untyped expression: %s", name.c_str() ); assertf( ! expr->get_args().empty(), "Cannot get function name from dereference with no arguments" ); return funcName( expr->get_args().front() ); } std::string funcName( Expression * func ) { if ( NameExpr * nameExpr = dynamic_cast< NameExpr * >( func ) ) { return nameExpr->get_name(); } else if ( VariableExpr * varExpr = dynamic_cast< VariableExpr * >( func ) ) { return varExpr->get_var()->get_name(); } else if ( CastExpr * castExpr = dynamic_cast< CastExpr * >( func ) ) { return funcName( castExpr->get_arg() ); } else if ( MemberExpr * memberExpr = dynamic_cast< MemberExpr * >( func ) ) { return memberExpr->get_member()->get_name(); } else if ( UntypedMemberExpr * memberExpr = dynamic_cast< UntypedMemberExpr * > ( func ) ) { return funcName( memberExpr->get_member() ); } else if ( UntypedExpr * untypedExpr = dynamic_cast< UntypedExpr * >( func ) ) { return handleDerefName( untypedExpr ); } else if ( ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * >( func ) ) { return handleDerefName( appExpr ); } else if ( ConstructorExpr * ctorExpr = dynamic_cast< ConstructorExpr * >( func ) ) { return funcName( getCallArg( ctorExpr->get_callExpr(), 0 ) ); } else { assertf( false, "Unexpected expression type being called as a function in call expression: %s", toString( func ).c_str() ); } } } std::string getFunctionName( Expression * expr ) { // there's some unforunate overlap here with getCalledFunction. Ideally this would be able to use getCalledFunction and // return the name of the DeclarationWithType, but this needs to work for NameExpr and UntypedMemberExpr, where getCalledFunction // can't possibly do anything reasonable. if ( ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * >( expr ) ) { return funcName( appExpr->get_function() ); } else if ( UntypedExpr * untypedExpr = dynamic_cast< UntypedExpr * > ( expr ) ) { return funcName( untypedExpr->get_function() ); } else { std::cerr << expr << std::endl; assertf( false, "Unexpected expression type passed to getFunctionName" ); } } Type * getPointerBase( Type * type ) { if ( PointerType * ptrType = dynamic_cast< PointerType * >( type ) ) { return ptrType->get_base(); } else if ( ArrayType * arrayType = dynamic_cast< ArrayType * >( type ) ) { return arrayType->get_base(); } else if ( ReferenceType * refType = dynamic_cast< ReferenceType * >( type ) ) { return refType->get_base(); } else { return nullptr; } } Type * isPointerType( Type * type ) { if ( getPointerBase( type ) ) return type; else return nullptr; } ApplicationExpr * createBitwiseAssignment( Expression * dst, Expression * src ) { static FunctionDecl * assign = nullptr; if ( ! assign ) { // temporary? Generate a fake assignment operator to represent bitwise assignments. // This operator could easily exist as a real function, but it's tricky because nothing should resolve to this function. TypeDecl * td = new TypeDecl( "T", noStorageClasses, nullptr, TypeDecl::Dtype, true ); assign = new FunctionDecl( "?=?", noStorageClasses, LinkageSpec::Intrinsic, SymTab::genAssignType( new TypeInstType( noQualifiers, td->name, td ) ), nullptr ); } if ( dynamic_cast< ReferenceType * >( dst->result ) ) { dst = new AddressExpr( dst ); } else { dst = new CastExpr( dst, new ReferenceType( noQualifiers, dst->result->clone() ) ); } if ( dynamic_cast< ReferenceType * >( src->result ) ) { src = new CastExpr( src, new ReferenceType( noQualifiers, src->result->stripReferences()->clone() ) ); } return new ApplicationExpr( VariableExpr::functionPointer( assign ), { dst, src } ); } class ConstExprChecker : public Visitor { public: ConstExprChecker() : isConstExpr( true ) {} using Visitor::visit; virtual void visit( ApplicationExpr * ) { isConstExpr = false; } virtual void visit( UntypedExpr * ) { isConstExpr = false; } virtual void visit( NameExpr * ) { isConstExpr = false; } // virtual void visit( CastExpr *castExpr ) { isConstExpr = false; } virtual void visit( AddressExpr *addressExpr ) { // address of a variable or member expression is constexpr Expression * arg = addressExpr->get_arg(); if ( ! dynamic_cast< NameExpr * >( arg) && ! dynamic_cast< VariableExpr * >( arg ) && ! dynamic_cast< MemberExpr * >( arg ) && ! dynamic_cast< UntypedMemberExpr * >( arg ) ) isConstExpr = false; } virtual void visit( UntypedMemberExpr * ) { isConstExpr = false; } virtual void visit( MemberExpr * ) { isConstExpr = false; } virtual void visit( VariableExpr * ) { isConstExpr = false; } // these might be okay? // virtual void visit( SizeofExpr *sizeofExpr ); // virtual void visit( AlignofExpr *alignofExpr ); // virtual void visit( UntypedOffsetofExpr *offsetofExpr ); // virtual void visit( OffsetofExpr *offsetofExpr ); // virtual void visit( OffsetPackExpr *offsetPackExpr ); // virtual void visit( AttrExpr *attrExpr ); // virtual void visit( CommaExpr *commaExpr ); // virtual void visit( LogicalExpr *logicalExpr ); // virtual void visit( ConditionalExpr *conditionalExpr ); virtual void visit( TypeExpr * ) { isConstExpr = false; } virtual void visit( AsmExpr * ) { isConstExpr = false; } virtual void visit( UntypedValofExpr * ) { isConstExpr = false; } virtual void visit( CompoundLiteralExpr * ) { isConstExpr = false; } virtual void visit( UntypedTupleExpr * ) { isConstExpr = false; } virtual void visit( TupleExpr * ) { isConstExpr = false; } virtual void visit( TupleAssignExpr * ) { isConstExpr = false; } bool isConstExpr; }; bool isConstExpr( Expression * expr ) { if ( expr ) { ConstExprChecker checker; expr->accept( checker ); return checker.isConstExpr; } return true; } bool isConstExpr( Initializer * init ) { if ( init ) { ConstExprChecker checker; init->accept( checker ); return checker.isConstExpr; } // if // for all intents and purposes, no initializer means const expr return true; } bool isConstructor( const std::string & str ) { return str == "?{}"; } bool isDestructor( const std::string & str ) { return str == "^?{}"; } bool isAssignment( const std::string & str ) { return str == "?=?"; } bool isCtorDtor( const std::string & str ) { return isConstructor( str ) || isDestructor( str ); } bool isCtorDtorAssign( const std::string & str ) { return isCtorDtor( str ) || isAssignment( str ); } FunctionDecl * isCopyFunction( Declaration * decl, const std::string & fname ) { FunctionDecl * function = dynamic_cast< FunctionDecl * >( decl ); if ( ! function ) return nullptr; if ( function->name != fname ) return nullptr; FunctionType * ftype = function->type; if ( ftype->parameters.size() != 2 ) return nullptr; Type * t1 = getPointerBase( ftype->get_parameters().front()->get_type() ); Type * t2 = ftype->parameters.back()->get_type(); assert( t1 ); if ( ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2, SymTab::Indexer() ) ) { return function; } else { return nullptr; } } FunctionDecl * isAssignment( Declaration * decl ) { return isCopyFunction( decl, "?=?" ); } FunctionDecl * isDestructor( Declaration * decl ) { if ( isDestructor( decl->get_name() ) ) { return dynamic_cast< FunctionDecl * >( decl ); } return nullptr; } FunctionDecl * isDefaultConstructor( Declaration * decl ) { if ( isConstructor( decl->name ) ) { if ( FunctionDecl * func = dynamic_cast< FunctionDecl * >( decl ) ) { if ( func->type->parameters.size() == 1 ) { return func; } } } return nullptr; } FunctionDecl * isCopyConstructor( Declaration * decl ) { return isCopyFunction( decl, "?{}" ); } }