// // Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo // // The contents of this file are covered under the licence agreement in the // file "LICENCE" distributed with Cforall. // // Autogen.cpp -- Generate automatic routines for data types. // // Author : Andrew Beach // Created On : Thu Dec 2 13:44:00 2021 // Last Modified By : Andrew Beach // Last Modified On : Thr Jan 27 9:29:00 2022 // Update Count : 1 // #include "Autogen.hpp" #include // for count_if #include // for strict_dynamic_cast, assert, assertf #include // for back_insert_iterator, back_inserter #include // for list, _List_iterator, list<>::iter... #include // for set, _Rb_tree_const_iterator #include // for pair #include // for vector #include "AST/Attribute.hpp" #include "AST/Decl.hpp" #include "AST/DeclReplacer.hpp" #include "AST/Expr.hpp" #include "AST/Pass.hpp" #include "AST/Stmt.hpp" #include "AST/SymbolTable.hpp" #include "CodeGen/OperatorTable.h" // for isCtorDtor, isCtorDtorAssign #include "Common/ScopedMap.h" // for ScopedMap<>::const_iterator, Scope... #include "Common/utility.h" // for cloneAll, operator+ #include "GenPoly/ScopedSet.h" // for ScopedSet, ScopedSet<>::iterator #include "InitTweak/GenInit.h" // for fixReturnStatements #include "InitTweak/InitTweak.h" // for isAssignment, isCopyConstructor #include "SymTab/Mangler.h" // for Mangler #include "CompilationState.h" // TODO: The other new ast function should be moved over to this file. #include "SymTab/Autogen.h" namespace Validate { namespace { // -------------------------------------------------------------------------- struct AutogenerateRoutines_new final : public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting { void previsit( const ast::EnumDecl * enumDecl ); void previsit( const ast::StructDecl * structDecl ); void previsit( const ast::UnionDecl * structDecl ); void previsit( const ast::TypeDecl * typeDecl ); void previsit( const ast::TraitDecl * traitDecl ); void previsit( const ast::FunctionDecl * functionDecl ); void postvisit( const ast::FunctionDecl * functionDecl ); private: // Current level of nested functions. unsigned int functionNesting = 0; }; // -------------------------------------------------------------------------- /// Class used to generate functions for a particular declaration. /// Note it isn't really stored, it is just a class for organization and to /// help pass around some of the common arguments. class FuncGenerator { public: std::list> forwards; std::list> definitions; FuncGenerator( const ast::Type * type, unsigned int functionNesting ) : type( type ), functionNesting( functionNesting ) {} /// Generate functions (and forward decls.) and append them to the list. void generateAndAppendFunctions( std::list> & ); virtual bool shouldAutogen() const = 0; protected: const ast::Type * type; unsigned int functionNesting; ast::Linkage::Spec proto_linkage = ast::Linkage::AutoGen; // Internal helpers: void genStandardFuncs(); void produceDecl( const ast::FunctionDecl * decl ); void produceForwardDecl( const ast::FunctionDecl * decl ); const CodeLocation& getLocation() const { return getDecl()->location; } ast::FunctionDecl * genProto( const std::string& name, std::vector>&& params, std::vector>&& returns ) const; ast::ObjectDecl * dstParam() const; ast::ObjectDecl * srcParam() const; ast::FunctionDecl * genCtorProto() const; ast::FunctionDecl * genCopyProto() const; ast::FunctionDecl * genDtorProto() const; ast::FunctionDecl * genAssignProto() const; ast::FunctionDecl * genFieldCtorProto( unsigned int fields ) const; // Internal Hooks: virtual void genFuncBody( ast::FunctionDecl * decl ) = 0; virtual void genFieldCtors() = 0; virtual bool isConcurrentType() const { return false; } virtual const ast::Decl * getDecl() const = 0; }; class StructFuncGenerator final : public FuncGenerator { const ast::StructDecl * decl; public: StructFuncGenerator( const ast::StructDecl * decl, const ast::StructInstType * type, unsigned int functionNesting ) : FuncGenerator( type, functionNesting ), decl( decl ) {} // Built-ins do not use autogeneration. bool shouldAutogen() const final { return !decl->linkage.is_builtin; } private: void genFuncBody( ast::FunctionDecl * decl ) final; void genFieldCtors() final; bool isConcurrentType() const final { return decl->is_thread() || decl->is_monitor(); } virtual const ast::Decl * getDecl() const final { return decl; } /// Generates a single struct member operation. /// (constructor call, destructor call, assignment call) // This is managed because it uses another helper that returns a ast::ptr. ast::ptr makeMemberOp( const CodeLocation& location, const ast::ObjectDecl * dstParam, const ast::Expr * src, const ast::ObjectDecl * field, ast::FunctionDecl * func, SymTab::LoopDirection direction ); /// Generates the body of a struct function by iterating the struct members /// (via parameters). Generates default constructor, copy constructor, /// copy assignment, and destructor bodies. No field constructor bodies. template void makeFunctionBody( Iterator member, Iterator end, ast::FunctionDecl * func, SymTab::LoopDirection direction ); /// Generate the body of a constructor which takes parameters that match /// fields. (With arguments for one to all of the fields.) template void makeFieldCtorBody( Iterator member, Iterator end, ast::FunctionDecl * func ); }; class UnionFuncGenerator final : public FuncGenerator { const ast::UnionDecl * decl; public: UnionFuncGenerator( const ast::UnionDecl * decl, const ast::UnionInstType * type, unsigned int functionNesting ) : FuncGenerator( type, functionNesting ), decl( decl ) {} // Built-ins do not use autogeneration. bool shouldAutogen() const final { return !decl->linkage.is_builtin; } private: void genFuncBody( ast::FunctionDecl * decl ) final; void genFieldCtors() final; const ast::Decl * getDecl() const final { return decl; } /// Generate a single union assignment expression (using memcpy). ast::ExprStmt * makeAssignOp( const CodeLocation& location, const ast::ObjectDecl * dstParam, const ast::ObjectDecl * srcParam ); }; class EnumFuncGenerator final : public FuncGenerator { const ast::EnumDecl * decl; public: EnumFuncGenerator( const ast::EnumDecl * decl, const ast::EnumInstType * type, unsigned int functionNesting ) : FuncGenerator( type, functionNesting ), decl( decl ) { // TODO: These functions are somewhere between instrinsic and autogen, // could possibly use a new linkage type. For now we just make them // intrinsic to code-gen them as C assignments. proto_linkage = ast::Linkage::Intrinsic; } bool shouldAutogen() const final { return true; } private: void genFuncBody( ast::FunctionDecl * decl ) final; void genFieldCtors() final; const ast::Decl * getDecl() const final { return decl; } }; class TypeFuncGenerator final : public FuncGenerator { const ast::TypeDecl * decl; public: TypeFuncGenerator( const ast::TypeDecl * decl, ast::TypeInstType * type, unsigned int functionNesting ) : FuncGenerator( type, functionNesting ), decl( decl ) {} bool shouldAutogen() const final { return true; } void genFieldCtors() final; private: void genFuncBody( ast::FunctionDecl * decl ) final; const ast::Decl * getDecl() const final { return decl; } }; // -------------------------------------------------------------------------- const std::vector>& getGenericParams( const ast::Type * t ) { if ( auto inst = dynamic_cast< const ast::StructInstType * >( t ) ) { return inst->base->params; } else if ( auto inst = dynamic_cast< const ast::UnionInstType * >( t ) ) { return inst->base->params; } static std::vector> const empty; return empty; } /// Changes the node inside a pointer so that it has the unused attribute. void addUnusedAttribute( ast::ptr & declPtr ) { ast::DeclWithType * decl = declPtr.get_and_mutate(); decl->attributes.push_back( new ast::Attribute( "unused" ) ); } // -------------------------------------------------------------------------- void AutogenerateRoutines_new::previsit( const ast::EnumDecl * enumDecl ) { // Must visit children (enum constants) to add them to the symbol table. if ( !enumDecl->body ) return; ast::EnumInstType enumInst( enumDecl->name ); enumInst.base = enumDecl; EnumFuncGenerator gen( enumDecl, &enumInst, functionNesting ); gen.generateAndAppendFunctions( declsToAddAfter ); } void AutogenerateRoutines_new::previsit( const ast::StructDecl * structDecl ) { visit_children = false; if ( !structDecl->body ) return; ast::StructInstType structInst( structDecl->name ); structInst.base = structDecl; for ( const ast::TypeDecl * typeDecl : structDecl->params ) { structInst.params.push_back( new ast::TypeExpr( typeDecl->location, new ast::TypeInstType( typeDecl ) ) ); } StructFuncGenerator gen( structDecl, &structInst, functionNesting ); gen.generateAndAppendFunctions( declsToAddAfter ); } void AutogenerateRoutines_new::previsit( const ast::UnionDecl * unionDecl ) { visit_children = false; if ( !unionDecl->body ) return; ast::UnionInstType unionInst( unionDecl->name ); unionInst.base = unionDecl; for ( const ast::TypeDecl * typeDecl : unionDecl->params ) { unionInst.params.push_back( new ast::TypeExpr( unionDecl->location, new ast::TypeInstType( typeDecl ) ) ); } UnionFuncGenerator gen( unionDecl, &unionInst, functionNesting ); gen.generateAndAppendFunctions( declsToAddAfter ); } /// Generate ctor/dtors/assign for typedecls, e.g., otype T = int *; void AutogenerateRoutines_new::previsit( const ast::TypeDecl * typeDecl ) { if ( !typeDecl->base ) return; ast::TypeInstType refType( typeDecl->name, typeDecl ); TypeFuncGenerator gen( typeDecl, &refType, functionNesting ); gen.generateAndAppendFunctions( declsToAddAfter ); } void AutogenerateRoutines_new::previsit( const ast::TraitDecl * ) { // Ensure that we don't add assignment ops for types defined as part of the trait visit_children = false; } void AutogenerateRoutines_new::previsit( const ast::FunctionDecl * ) { // Track whether we're currently in a function. // Can ignore function type idiosyncrasies, because function type can never // declare a new type. functionNesting += 1; } void AutogenerateRoutines_new::postvisit( const ast::FunctionDecl * ) { functionNesting -= 1; } void FuncGenerator::generateAndAppendFunctions( std::list> & decls ) { if ( !shouldAutogen() ) return; // Generate the functions (they go into forwards and definitions). genStandardFuncs(); genFieldCtors(); // Now export the lists contents. decls.splice( decls.end(), forwards ); decls.splice( decls.end(), definitions ); } void FuncGenerator::produceDecl( const ast::FunctionDecl * decl ) { assert( nullptr != decl->stmts ); definitions.push_back( decl ); } /// Make a forward declaration of the decl and add it to forwards. void FuncGenerator::produceForwardDecl( const ast::FunctionDecl * decl ) { if (0 != functionNesting) return; ast::FunctionDecl * fwd = ast::deepCopy( decl ); fwd->stmts = nullptr; fwd->fixUniqueId(); forwards.push_back( fwd ); } /// Generates a basic prototype function declaration. ast::FunctionDecl * FuncGenerator::genProto( const std::string& name, std::vector>&& params, std::vector>&& returns ) const { // Handle generic prameters and assertions, if any. auto const & old_type_params = getGenericParams( type ); std::vector> type_params; std::vector> assertions; for ( auto & old_param : old_type_params ) { ast::TypeDecl * decl = ast::deepCopy( old_param ); for ( auto assertion : decl->assertions ) { assertions.push_back( assertion ); } decl->assertions.clear(); type_params.push_back( decl ); } // TODO: The values in params and returns still may point at the old // generic params, that does not appear to be an issue but perhaps it // should be addressed. ast::FunctionDecl * decl = new ast::FunctionDecl( // Auto-generated routines use the type declaration's location. getLocation(), name, std::move( type_params ), std::move( assertions ), std::move( params ), std::move( returns ), // Only a prototype, no body. nullptr, // Use static storage if we are at the top level. (0 < functionNesting) ? ast::Storage::Classes() : ast::Storage::Static, proto_linkage, std::vector>(), // Auto-generated routines are inline to avoid conflicts. ast::Function::Specs( ast::Function::Inline ) ); decl->fixUniqueId(); return decl; } ast::ObjectDecl * FuncGenerator::dstParam() const { return new ast::ObjectDecl( getLocation(), "_dst", new ast::ReferenceType( ast::deepCopy( type ) ), nullptr, {}, ast::Linkage::Cforall ); } ast::ObjectDecl * FuncGenerator::srcParam() const { return new ast::ObjectDecl( getLocation(), "_src", ast::deepCopy( type ), nullptr, {}, ast::Linkage::Cforall ); } /// Use the current type T to create `void ?{}(T & _dst)`. ast::FunctionDecl * FuncGenerator::genCtorProto() const { return genProto( "?{}", { dstParam() }, {} ); } /// Use the current type T to create `void ?{}(T & _dst, T _src)`. ast::FunctionDecl * FuncGenerator::genCopyProto() const { return genProto( "?{}", { dstParam(), srcParam() }, {} ); } /// Use the current type T to create `void ?{}(T & _dst)`. ast::FunctionDecl * FuncGenerator::genDtorProto() const { // The destructor must be mutex on a concurrent type. auto dst = dstParam(); if ( isConcurrentType() ) { add_qualifiers( dst->type, ast::CV::Qualifiers( ast::CV::Mutex ) ); } return genProto( "^?{}", { dst }, {} ); } /// Use the current type T to create `T ?{}(T & _dst, T _src)`. ast::FunctionDecl * FuncGenerator::genAssignProto() const { // Only the name is different, so just reuse the generation function. auto retval = srcParam(); retval->name = "_ret"; // xxx - Adding this unused attribute can slience unused variable warning // However, some code might not be compiled as expected // Temporarily disabled // retval->attributes.push_back(new ast::Attribute("unused")); return genProto( "?=?", { dstParam(), srcParam() }, { retval } ); } // This one can return null if the last field is an unnamed bitfield. ast::FunctionDecl * FuncGenerator::genFieldCtorProto( unsigned int fields ) const { assert( 0 < fields ); auto aggr = strict_dynamic_cast( getDecl() ); std::vector> params = { dstParam() }; for ( unsigned int index = 0 ; index < fields ; ++index ) { auto member = aggr->members[index].strict_as(); if ( SymTab::isUnnamedBitfield( dynamic_cast( member ) ) ) { if ( index == fields - 1 ) { return nullptr; } continue; } auto * paramType = ast::deepCopy( member->get_type() ); paramType->attributes.clear(); ast::ObjectDecl * param = new ast::ObjectDecl( getLocation(), member->name, paramType ); param->linkage = ast::Linkage::Cforall; for ( auto & attr : member->attributes ) { if ( attr->isValidOnFuncParam() ) { param->attributes.push_back( attr ); } } params.emplace_back( param ); } return genProto( "?{}", std::move( params ), {} ); } void appendReturnThis( ast::FunctionDecl * decl ) { assert( 1 <= decl->params.size() ); assert( 1 == decl->returns.size() ); assert( decl->stmts ); const CodeLocation& location = (decl->stmts->kids.empty()) ? decl->stmts->location : decl->stmts->kids.back()->location; const ast::DeclWithType * thisParam = decl->params.front(); decl->stmts.get_and_mutate()->push_back( new ast::ReturnStmt( location, new ast::VariableExpr( location, thisParam ) ) ); } void FuncGenerator::genStandardFuncs() { // The order here determines the order that these functions are generated. // Assignment should come last since it uses copy constructor in return. ast::FunctionDecl *(FuncGenerator::*standardProtos[4])() const = { &FuncGenerator::genCtorProto, &FuncGenerator::genCopyProto, &FuncGenerator::genDtorProto, &FuncGenerator::genAssignProto }; for ( auto & generator : standardProtos ) { ast::FunctionDecl * decl = (this->*generator)(); produceForwardDecl( decl ); genFuncBody( decl ); if ( CodeGen::isAssignment( decl->name ) ) { appendReturnThis( decl ); } produceDecl( decl ); } } void StructFuncGenerator::genFieldCtors() { // The field constructors are only generated if the default constructor // and copy constructor are both generated, since the need both. unsigned numCtors = std::count_if( definitions.begin(), definitions.end(), [](const ast::Decl * decl){ return CodeGen::isConstructor( decl->name ); } ); if ( 2 != numCtors ) return; for ( unsigned int num = 1 ; num <= decl->members.size() ; ++num ) { ast::FunctionDecl * ctor = genFieldCtorProto( num ); if ( nullptr == ctor ) { continue; } produceForwardDecl( ctor ); makeFieldCtorBody( decl->members.begin(), decl->members.end(), ctor ); produceDecl( ctor ); } } void StructFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) { // Generate appropriate calls to member constructors and assignment. // Destructor needs to do everything in reverse, // so pass "forward" based on whether the function is a destructor if ( CodeGen::isDestructor( functionDecl->name ) ) { makeFunctionBody( decl->members.rbegin(), decl->members.rend(), functionDecl, SymTab::LoopBackward ); } else { makeFunctionBody( decl->members.begin(), decl->members.end(), functionDecl, SymTab::LoopForward ); } } ast::ptr StructFuncGenerator::makeMemberOp( const CodeLocation& location, const ast::ObjectDecl * dstParam, const ast::Expr * src, const ast::ObjectDecl * field, ast::FunctionDecl * func, SymTab::LoopDirection direction ) { InitTweak::InitExpander_new srcParam( src ); // Assign to destination. ast::Expr * dstSelect = new ast::MemberExpr( location, field, new ast::CastExpr( location, new ast::VariableExpr( location, dstParam ), dstParam->type.strict_as()->base ) ); return genImplicitCall( srcParam, dstSelect, location, func->name, field, direction ); } template void StructFuncGenerator::makeFunctionBody( Iterator current, Iterator end, ast::FunctionDecl * func, SymTab::LoopDirection direction ) { // Trying to get the best code location. Should probably use a helper or // just figure out what that would be given where this is called. assert( nullptr == func->stmts ); const CodeLocation& location = func->location; ast::CompoundStmt * stmts = new ast::CompoundStmt( location ); for ( ; current != end ; ++current ) { const ast::ptr & member = *current; auto field = member.as(); if ( nullptr == field ) { continue; } // Don't assign to constant members (but do construct/destruct them). if ( CodeGen::isAssignment( func->name ) ) { // For array types we need to strip off the array layers. const ast::Type * type = field->get_type(); while ( auto at = dynamic_cast( type ) ) { type = at->base; } if ( type->is_const() ) { continue; } } assert( !func->params.empty() ); const ast::ObjectDecl * dstParam = func->params.front().strict_as(); const ast::ObjectDecl * srcParam = nullptr; if ( 2 == func->params.size() ) { srcParam = func->params.back().strict_as(); } ast::Expr * srcSelect = (srcParam) ? new ast::MemberExpr( location, field, new ast::VariableExpr( location, srcParam ) ) : nullptr; ast::ptr stmt = makeMemberOp( location, dstParam, srcSelect, field, func, direction ); if ( nullptr != stmt ) { stmts->kids.push_back( stmt ); } } func->stmts = stmts; } template void StructFuncGenerator::makeFieldCtorBody( Iterator current, Iterator end, ast::FunctionDecl * func ) { const CodeLocation& location = func->location; auto & params = func->params; // Need at least the constructed parameter and one field parameter. assert( 2 <= params.size() ); ast::CompoundStmt * stmts = new ast::CompoundStmt( location ); auto dstParam = params.front().strict_as(); // Skip over the 'this' parameter. for ( auto param = params.begin() + 1 ; current != end ; ++current ) { const ast::ptr & member = *current; ast::ptr stmt = nullptr; auto field = member.as(); // Not sure why it could be null. // Don't make a function for a parameter that is an unnamed bitfield. if ( nullptr == field || SymTab::isUnnamedBitfield( field ) ) { continue; // Matching Parameter: Initialize the field by copy. } else if ( params.end() != param ) { const ast::Expr *srcSelect = new ast::VariableExpr( func->location, param->get() ); stmt = makeMemberOp( location, dstParam, srcSelect, field, func, SymTab::LoopForward ); ++param; // No Matching Parameter: Initialize the field by default constructor. } else { stmt = makeMemberOp( location, dstParam, nullptr, field, func, SymTab::LoopForward ); } if ( nullptr != stmt ) { stmts->kids.push_back( stmt ); } } func->stmts = stmts; } void UnionFuncGenerator::genFieldCtors() { // Field constructors are only generated if default and copy constructor // are generated, since they need access to both unsigned numCtors = std::count_if( definitions.begin(), definitions.end(), []( const ast::Decl * d ){ return CodeGen::isConstructor( d->name ); } ); if ( 2 != numCtors ) { return; } // Create a constructor which takes the first member type as a // parameter. For example for `union A { int x; char y; };` generate // a function with signature `void ?{}(A *, int)`. This mimics C's // behaviour which initializes the first member of the union. // Still, there must be some members. if ( !decl->members.empty() ) { ast::FunctionDecl * ctor = genFieldCtorProto( 1 ); if ( nullptr == ctor ) { return; } produceForwardDecl( ctor ); auto params = ctor->params; auto dstParam = params.front().strict_as(); auto srcParam = params.back().strict_as(); ctor->stmts = new ast::CompoundStmt( getLocation(), { makeAssignOp( getLocation(), dstParam, srcParam ) } ); produceDecl( ctor ); } } void UnionFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) { const CodeLocation& location = functionDecl->location; auto & params = functionDecl->params; if ( InitTweak::isCopyConstructor( functionDecl ) || InitTweak::isAssignment( functionDecl ) ) { assert( 2 == params.size() ); auto dstParam = params.front().strict_as(); auto srcParam = params.back().strict_as(); functionDecl->stmts = new ast::CompoundStmt( location, { makeAssignOp( location, dstParam, srcParam ) } ); } else { assert( 1 == params.size() ); // Default constructor and destructor is empty. functionDecl->stmts = new ast::CompoundStmt( location ); // Add unused attribute to parameter to silence warnings. addUnusedAttribute( params.front() ); // Just an extra step to make the forward and declaration match. if ( forwards.empty() ) return; ast::FunctionDecl * fwd = strict_dynamic_cast( forwards.back().get_and_mutate() ); addUnusedAttribute( fwd->params.front() ); } } ast::ExprStmt * UnionFuncGenerator::makeAssignOp( const CodeLocation& location, const ast::ObjectDecl * dstParam, const ast::ObjectDecl * srcParam ) { return new ast::ExprStmt( location, new ast::UntypedExpr( location, new ast::NameExpr( location, "__builtin_memcpy" ), { new ast::AddressExpr( location, new ast::VariableExpr( location, dstParam ) ), new ast::AddressExpr( location, new ast::VariableExpr( location, srcParam ) ), new ast::SizeofExpr( location, srcParam->type ), } ) ); } void EnumFuncGenerator::genFieldCtors() { // Enumerations to not have field constructors. } void EnumFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) { const CodeLocation& location = functionDecl->location; auto & params = functionDecl->params; if ( InitTweak::isCopyConstructor( functionDecl ) || InitTweak::isAssignment( functionDecl ) ) { assert( 2 == params.size() ); auto dstParam = params.front().strict_as(); auto srcParam = params.back().strict_as(); /* This looks like a recursive call, but code-gen will turn it into * a C-style assignment. * * This is still before function pointer type conversion, * so this will have to do it manually. * * It will also reference the parent function declaration, creating * a cycle for references. This also means that the ref-counts are * now non-zero and the declaration will be deleted if it ever * returns to zero. */ auto callExpr = new ast::ApplicationExpr( location, ast::VariableExpr::functionPointer( location, functionDecl ), { new ast::VariableExpr( location, dstParam ), new ast::VariableExpr( location, srcParam ), } ); functionDecl->stmts = new ast::CompoundStmt( location, { new ast::ExprStmt( location, callExpr ) } ); } else { assert( 1 == params.size() ); // Default constructor and destructor is empty. functionDecl->stmts = new ast::CompoundStmt( location ); // Just add unused attribute to parameter to silence warnings. addUnusedAttribute( params.front() ); // Just an extra step to make the forward and declaration match. if ( forwards.empty() ) return; ast::FunctionDecl * fwd = strict_dynamic_cast( forwards.back().get_and_mutate() ); addUnusedAttribute( fwd->params.front() ); } } void TypeFuncGenerator::genFieldCtors() { // Opaque types do not have field constructors. } void TypeFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) { const CodeLocation& location = functionDecl->location; auto & params = functionDecl->type->params; assertf( 1 == params.size() || 2 == params.size(), "Incorrect number of parameters in autogenerated typedecl function: %zd", params.size() ); auto dstParam = params.front().strict_as(); auto srcParam = (2 == params.size()) ? params.back().strict_as() : nullptr; // Generate appropriate calls to member constructor and assignment. ast::UntypedExpr * expr = new ast::UntypedExpr( location, new ast::NameExpr( location, functionDecl->name ) ); expr->args.push_back( new ast::CastExpr( location, new ast::VariableExpr( location, dstParam ), new ast::ReferenceType( decl->base ) ) ); if ( srcParam ) { expr->args.push_back( new ast::CastExpr( location, new ast::VariableExpr( location, srcParam ), decl->base ) ); } functionDecl->stmts = new ast::CompoundStmt( location, { new ast::ExprStmt( location, expr ) } ); } } // namespace void autogenerateRoutines( ast::TranslationUnit & translationUnit ) { ast::Pass::run( translationUnit ); } } // Validate // Local Variables: // // tab-width: 4 // // mode: c++ // // compile-command: "make install" // // End: //