// // 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. // // Indexer.cc -- // // Author : Richard C. Bilson // Created On : Sun May 17 21:37:33 2015 // Last Modified By : Aaron B. Moss // Last Modified On : Fri Mar 8 13:55:00 2019 // Update Count : 21 // #include "Indexer.h" #include // for assert, strict_dynamic_cast #include // for operator<<, basic_ostream, ostream #include // for string, operator<<, operator!= #include // for shared_ptr, make_shared #include // for operator!=, unordered_map<>::const... #include // for unordered_set #include // for pair, make_pair, move #include "CodeGen/OperatorTable.h" // for isCtorDtor, isCtorDtorAssign #include "Common/SemanticError.h" // for SemanticError #include "Common/utility.h" // for cloneAll #include "Common/Stats/Counter.h" // for counters #include "GenPoly/GenPoly.h" // for getFunctionType #include "InitTweak/InitTweak.h" // for isConstructor, isCopyFunction, isC... #include "Mangler.h" // for Mangler #include "Parser/LinkageSpec.h" // for isMangled, isOverridable, Spec #include "ResolvExpr/typeops.h" // for typesCompatible #include "SynTree/Constant.h" // for Constant #include "SynTree/Declaration.h" // for DeclarationWithType, FunctionDecl #include "SynTree/Expression.h" // for Expression, ImplicitCopyCtorExpr #include "SynTree/Initializer.h" // for Initializer #include "SynTree/Statement.h" // for CompoundStmt, Statement, ForStmt (... #include "SynTree/Type.h" // for Type, StructInstType, UnionInstType #define debugPrint(x) if ( doDebug ) { std::cerr << x; } namespace SymTab { // Statistics block // namespace { // static inline auto stats_idtable() { // using namespace Stats::Counters; // static auto group = build("IdTable"); // static struct { // SimpleCounter * find; // AverageCounter * size; // AverageCounter * key; // } ret = { // .find = build("Find calls", group), // .size = build>("Average Size", group), // .key = build>("Average Key Size", group), // }; // return ret; // } // static inline auto stats_indexers() { // using namespace Stats::Counters; // static auto group = build("Indexers"); // static struct { // SimpleCounter * count; // AverageCounter * size; // AverageCounter * depth_a; // MaxCounter * depth_m; // SimpleCounter * new_scopes; // AverageCounter * avg_scope_depth; // MaxCounter * max_scope_depth; // SimpleCounter * add_calls; // SimpleCounter * lookup_calls; // SimpleCounter * map_lookups; // } ret = { // .count = build("Count", group), // .size = build>("Average Size", group), // .depth_a = build>("Average Depth", group), // .depth_m = build>("Max Depth", group), // .new_scopes = build("Scopes", group), // .avg_scope_depth = build>("Average Scope", group), // .max_scope_depth = build>("Max Scope", group), // .add_calls = build("Add Calls", group), // .lookup_calls = build("Lookup Calls", group), // .map_lookups = build("Map Lookups", group), // }; // return ret; // } // } // std::ostream & operator<<( std::ostream & out, const Indexer::IdData & data ) { // return out << "(" << data.id << "," << data.baseExpr << ")"; // } // void dump( const Indexer::IdTable &table, std::ostream &os ) { // for ( IdTable::const_iterator id = table.begin(); id != table.end(); ++id ) { // for ( MangleTable::const_iterator mangle = id->second.begin(); mangle != id->second.end(); ++mangle ) { // os << mangle->second << std::endl; // } // } // } // template< typename Decl > // void dump( const PersistentMap< std::string, Decl* > &table, std::ostream &os ) { // for ( auto decl : table ) { // os << decl.second << std::endl; // } // for // } void Indexer::removeSpecialOverrides( const std::string &id, std::list< IdData > & out ) const { // only need to perform this step for constructors, destructors, and assignment functions if ( ! CodeGen::isCtorDtorAssign( id ) ) return; // helpful data structure to organize properties for a type struct ValueType { struct DeclBall { // properties for this particular decl IdData decl; bool isUserDefinedFunc; bool isCopyFunc; }; // properties for this type bool existsUserDefinedCopyFunc = false; // user-defined copy ctor found BaseSyntaxNode * deleteStmt = nullptr; // non-null if a user-defined function is found std::list< DeclBall > decls; // another FunctionDecl for the current type was found - determine // if it has special properties and update data structure accordingly ValueType & operator+=( IdData data ) { DeclarationWithType * function = data.id; bool isUserDefinedFunc = ! LinkageSpec::isOverridable( function->linkage ); bool isCopyFunc = InitTweak::isCopyFunction( function, function->name ); decls.push_back( DeclBall{ data, isUserDefinedFunc, isCopyFunc } ); existsUserDefinedCopyFunc |= (isUserDefinedFunc && isCopyFunc); if ( isUserDefinedFunc && ! deleteStmt ) { // any user-defined function can act as an implicit delete statement for generated constructors. // a delete stmt should not act as an implicit delete statement. deleteStmt = data.id; } return *this; } }; // ValueType std::list< IdData > copy; copy.splice( copy.end(), out ); // organize discovered declarations by type std::unordered_map< std::string, ValueType > funcMap; for ( auto decl : copy ) { if ( FunctionDecl * function = dynamic_cast< FunctionDecl * >( decl.id ) ) { std::list< DeclarationWithType * > & params = function->type->parameters; assert( ! params.empty() ); // use base type of pointer, so that qualifiers on the pointer type aren't considered. Type * base = InitTweak::getPointerBase( params.front()->get_type() ); assert( base ); funcMap[ Mangler::mangle( base ) ] += decl; } else { out.push_back( decl ); } } // if a type contains user defined ctor/dtor/assign, then special rules trigger, which determine // the set of ctor/dtor/assign that can be used by the requester. In particular, if the user defines // a default ctor, then the generated default ctor is unavailable, likewise for copy ctor // and dtor. If the user defines any ctor/dtor, then no generated field ctors are available. // If the user defines any ctor then the generated default ctor is unavailable (intrinsic default // ctor must be overridden exactly). If the user defines anything that looks like a copy constructor, // then the generated copy constructor is unavailable, and likewise for the assignment operator. for ( std::pair< const std::string, ValueType > & pair : funcMap ) { ValueType & val = pair.second; for ( ValueType::DeclBall ball : val.decls ) { bool isNotUserDefinedFunc = ! ball.isUserDefinedFunc && ball.decl.id->linkage != LinkageSpec::Intrinsic; bool isCopyFunc = ball.isCopyFunc; bool existsUserDefinedCopyFunc = val.existsUserDefinedCopyFunc; // only implicitly delete non-user defined functions that are not intrinsic, and are // not copy functions (assignment or copy constructor). If a user-defined copy function exists, // do not pass along the non-user-defined copy functions since signatures do not have to match, // and the generated functions will often be cheaper. if ( isNotUserDefinedFunc ) { if ( isCopyFunc ) { // Skip over non-user-defined copy functions when there is a user-defined copy function. // Since their signatures do not have to be exact, deleting them is the wrong choice. if ( existsUserDefinedCopyFunc ) continue; } else { // delete non-user-defined non-copy functions if applicable. // deleteStmt will be non-null only if a user-defined function is found. ball.decl.deleteStmt = val.deleteStmt; } } out.push_back( ball.decl ); } } } Indexer::Indexer() : idTable(), typeTable(), structTable(), enumTable(), unionTable(), traitTable(), prevScope(), scope( 0 ) {} Indexer::~Indexer() {} void Indexer::enterScope() { // save current state in prevScope and increment scope prevScope = std::make_shared( *this ); ++scope; // if ( doDebug ) { // std::cerr << "--- Entering scope " << scope << std::endl; // } } void Indexer::leaveScope() { // if ( doDebug ) { // std::cerr << "--- Leaving scope " << scope << " containing" << std::endl; // dump( idTable, std::cerr ); // dump( typeTable, std::cerr ); // dump( structTable, std::cerr ); // dump( enumTable, std::cerr ); // dump( unionTable, std::cerr ); // dump( traitTable, std::cerr ); // } // replace all maps and scope index with previous scope's versions *this = *prevScope; } void Indexer::lookupId( const std::string &id, std::list< IdData > &out ) const { if ( ! idTable ) return; auto decls = idTable->find( id ); if ( decls == idTable->end() ) return; for ( auto decl : *(decls->second) ) { out.push_back( decl.second ); } // some special functions, e.g. constructors and destructors // remove autogenerated functions when they are defined so that // they can never be matched removeSpecialOverrides( id, out ); } NamedTypeDecl *Indexer::lookupType( const std::string &id ) const { if ( ! typeTable ) return nullptr; auto it = typeTable->find( id ); return it == typeTable->end() ? nullptr : it->second.decl; } StructDecl *Indexer::lookupStruct( const std::string &id ) const { if ( ! structTable ) return nullptr; auto it = structTable->find( id ); return it == structTable->end() ? nullptr : it->second.decl; } EnumDecl *Indexer::lookupEnum( const std::string &id ) const { if ( ! enumTable ) return nullptr; auto it = enumTable->find( id ); return it == enumTable->end() ? nullptr : it->second.decl; } UnionDecl *Indexer::lookupUnion( const std::string &id ) const { if ( ! unionTable ) return nullptr; auto it = unionTable->find( id ); return it == unionTable->end() ? nullptr : it->second.decl; } TraitDecl *Indexer::lookupTrait( const std::string &id ) const { if ( ! traitTable ) return nullptr; auto it = traitTable->find( id ); return it == traitTable->end() ? nullptr : it->second.decl; } const Indexer* Indexer::atScope( unsigned long scope ) const { // scan back to scope; guaranteed one indexer per scope by construction of enterScope, // final indexer in list has scope 0, cannot be > scope const Indexer* indexer = this; while ( indexer->scope > scope ) { indexer = indexer->prevScope.get(); } return indexer; } // const Indexer::IdData * Indexer::localLookupId( // const std::string &id, const std::string &mangleName ) const { // if ( ! idTable ) return nullptr; // // lookup name // auto decls = idTable->find( id ); // if ( decls == idTable->end() ) return nullptr; // // lookup mangleName // // assume any mangle-table pointer added is non-null // const MangleTable& mangleTable = *(decls->second); // auto decl = mangleTable.find( mangleName ); // if ( decl == mangleTable.end() ) return nullptr; // // skip identifiers not defined in this scope // if ( decl->second.scope != scope ) return nullptr; // return &decl->second; // } // Indexer::IdData * Indexer::lookupIdAtScope( // const std::string &id, const std::string &mangleName, unsigned long scope ) { // return const_cast( // const_cast(this)->lookupIdAtScope( id, mangleName, scope )); // } // NamedTypeDecl *Indexer::lookupTypeAtScope( const std::string &id, unsigned long scope ) const { // if ( ! tables ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope < scope ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope > scope ) return tables->base.lookupTypeAtScope( id, scope ); // ++*stats_indexers().map_lookups; // TypeTable::const_iterator ret = tables->typeTable.find( id ); // return ret != tables->typeTable.end() ? // ++*stats_indexers().lookup_calls, ret->second : // tables->base.lookupTypeAtScope( id, scope ); // } // StructDecl *Indexer::lookupStructAtScope( const std::string &id, unsigned long scope ) const { // if ( ! tables ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope < scope ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope > scope ) return tables->base.lookupStructAtScope( id, scope ); // ++*stats_indexers().map_lookups; // StructTable::const_iterator ret = tables->structTable.find( id ); // return ret != tables->structTable.end() ? // ++*stats_indexers().lookup_calls, ret->second : // tables->base.lookupStructAtScope( id, scope ); // } // EnumDecl *Indexer::lookupEnumAtScope( const std::string &id, unsigned long scope ) const { // if ( ! tables ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope < scope ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope > scope ) return tables->base.lookupEnumAtScope( id, scope ); // ++*stats_indexers().map_lookups; // EnumTable::const_iterator ret = tables->enumTable.find( id ); // return ret != tables->enumTable.end() ? // ++*stats_indexers().lookup_calls, ret->second : // tables->base.lookupEnumAtScope( id, scope ); // } // UnionDecl *Indexer::lookupUnionAtScope( const std::string &id, unsigned long scope ) const { // if ( ! tables ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope < scope ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope > scope ) return tables->base.lookupUnionAtScope( id, scope ); // ++*stats_indexers().map_lookups; // UnionTable::const_iterator ret = tables->unionTable.find( id ); // return ret != tables->unionTable.end() ? // ++*stats_indexers().lookup_calls, ret->second : // tables->base.lookupUnionAtScope( id, scope ); // } // TraitDecl *Indexer::lookupTraitAtScope( const std::string &id, unsigned long scope ) const { // if ( ! tables ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope < scope ) return ++*stats_indexers().lookup_calls, nullptr; // if ( tables->scope > scope ) return tables->base.lookupTraitAtScope( id, scope ); // ++*stats_indexers().map_lookups; // TraitTable::const_iterator ret = tables->traitTable.find( id ); // return ret != tables->traitTable.end() ? // ++*stats_indexers().lookup_calls, ret->second : // tables->base.lookupTraitAtScope( id, scope ); // } NamedTypeDecl *Indexer::globalLookupType( const std::string &id ) const { return atScope( 0 )->lookupType( id ); } StructDecl *Indexer::globalLookupStruct( const std::string &id ) const { return atScope( 0 )->lookupStruct( id ); } UnionDecl *Indexer::globalLookupUnion( const std::string &id ) const { return atScope( 0 )->lookupUnion( id ); } EnumDecl *Indexer::globalLookupEnum( const std::string &id ) const { return atScope( 0 )->lookupEnum( id ); } bool isFunction( DeclarationWithType * decl ) { return GenPoly::getFunctionType( decl->get_type() ); } bool isObject( DeclarationWithType * decl ) { return ! isFunction( decl ); } bool isDefinition( DeclarationWithType * decl ) { if ( FunctionDecl * func = dynamic_cast< FunctionDecl * >( decl ) ) { // a function is a definition if it has a body return func->statements; } else { // an object is a definition if it is not marked extern. // both objects must be marked extern return ! decl->get_storageClasses().is_extern; } } bool Indexer::addedIdConflicts( const Indexer::IdData & existing, DeclarationWithType *added, Indexer::OnConflict handleConflicts, BaseSyntaxNode * deleteStmt ) { // if we're giving the same name mangling to things of different types then there is something wrong assert( (isObject( added ) && isObject( existing.id ) ) || ( isFunction( added ) && isFunction( existing.id ) ) ); if ( LinkageSpec::isOverridable( existing.id->linkage ) ) { // new definition shadows the autogenerated one, even at the same scope return false; } else if ( LinkageSpec::isMangled( added->linkage ) || ResolvExpr::typesCompatible( added->get_type(), existing.id->get_type(), Indexer() ) ) { // it is a conflict if one declaration is deleted and the other is not if ( deleteStmt && ! existing.deleteStmt ) { if ( handleConflicts.mode == OnConflict::Error ) { SemanticError( added, "deletion of defined identifier " ); } return true; } else if ( ! deleteStmt && existing.deleteStmt ) { if ( handleConflicts.mode == OnConflict::Error ) { SemanticError( added, "definition of deleted identifier " ); } return true; } if ( isDefinition( added ) && isDefinition( existing.id ) ) { if ( handleConflicts.mode == OnConflict::Error ) { SemanticError( added, isFunction( added ) ? "duplicate function definition for " : "duplicate object definition for " ); } return true; } // if } else { if ( handleConflicts.mode == OnConflict::Error ) { SemanticError( added, "duplicate definition for " ); } return true; } // if return true; } bool Indexer::hasCompatibleCDecl( const std::string &id, const std::string &mangleName ) const { if ( ! idTable ) return false; auto decls = idTable->find( id ); if ( decls == idTable->end() ) return false; for ( auto decl : *(decls->second) ) { // skip other scopes (hidden by this decl) if ( decl.second.scope != scope ) continue; // check for C decl with compatible type (by mangleName) if ( ! LinkageSpec::isMangled( decl.second.id->linkage ) && decl.first == mangleName ) { return true; } } return false; } bool Indexer::hasIncompatibleCDecl( const std::string &id, const std::string &mangleName ) const { if ( ! idTable ) return false; auto decls = idTable->find( id ); if ( decls == idTable->end() ) return false; for ( auto decl : *(decls->second) ) { // skip other scopes (hidden by this decl) if ( decl.second.scope != scope ) continue; // check for C decl with incompatible type (by manglename) if ( ! LinkageSpec::isMangled( decl.second.id->linkage ) && decl.first != mangleName ) { return true; } } return false; } void Indexer::addId( DeclarationWithType *decl, OnConflict handleConflicts, Expression * baseExpr, BaseSyntaxNode * deleteStmt ) { if ( decl->name == "" ) return; // debugPrint( "Adding Id " << decl->name << std::endl ); const std::string &name = decl->name; std::string mangleName; if ( LinkageSpec::isOverridable( decl->linkage ) ) { // mangle the name without including the appropriate suffix, so overridable routines // are placed into the same "bucket" as their user defined versions. mangleName = Mangler::mangle( decl, false ); } else { mangleName = Mangler::mangle( decl ); } // if // this ensures that no two declarations with the same unmangled name at the same scope // both have C linkage if ( LinkageSpec::isMangled( decl->linkage ) ) { // Check that a Cforall declaration doesn't override any C declaration if ( hasCompatibleCDecl( name, mangleName ) ) { SemanticError( decl, "Cforall declaration hides C function " ); } } else { // NOTE: only correct if name mangling is completely isomorphic to C // type-compatibility, which it may not be. if ( hasIncompatibleCDecl( name, mangleName ) ) { SemanticError( decl, "conflicting overload of C function " ); } } // ensure tables exist and add identifier MangleTable::Ptr mangleTable; if ( ! idTable ) { idTable = IdTable::new_ptr(); mangleTable = MangleTable::new_ptr(); } else { auto decls = idTable->find( name ); if ( decls == idTable->end() ) { mangleTable = MangleTable::new_ptr(); } else { mangleTable = decls->second; // skip in-scope repeat declarations of same identifier auto existing = mangleTable->find( mangleName ); if ( existing != mangleTable->end() && existing->second.scope == scope && existing->second.id ) { if ( addedIdConflicts( existing->second, decl, handleConflicts, deleteStmt ) ) { if ( handleConflicts.mode == OnConflict::Delete ) { // set delete expression for conflicting identifier idTable = idTable->set( name, mangleTable->set( mangleName, IdData{ existing->second, handleConflicts.deleteStmt } ) ); } return; } } } } // add/overwrite with new identifier idTable = idTable->set( name, mangleTable->set( mangleName, IdData{ decl, baseExpr, deleteStmt, scope } ) ); } void Indexer::addId( DeclarationWithType * decl, Expression * baseExpr ) { // default handling of conflicts is to raise an error addId( decl, OnConflict::error(), baseExpr, decl->isDeleted ? decl : nullptr ); } void Indexer::addDeletedId( DeclarationWithType * decl, BaseSyntaxNode * deleteStmt ) { // default handling of conflicts is to raise an error addId( decl, OnConflict::error(), nullptr, deleteStmt ); } bool addedTypeConflicts( NamedTypeDecl *existing, NamedTypeDecl *added ) { if ( existing->base == nullptr ) { return false; } else if ( added->base == nullptr ) { return true; } else { assert( existing->base && added->base ); // typedef redeclarations are errors only if types are different if ( ! ResolvExpr::typesCompatible( existing->base, added->base, Indexer() ) ) { SemanticError( added->location, "redeclaration of " + added->name ); } } // does not need to be added to the table if both existing and added have a base that are the same return true; } void Indexer::addType( NamedTypeDecl *decl ) { const std::string &id = decl->name; if ( ! typeTable ) { typeTable = TypeTable::new_ptr(); } else { auto existing = typeTable->find( id ); if ( existing != typeTable->end() && existing->second.scope == scope && addedTypeConflicts( existing->second.decl, decl ) ) return; } typeTable = typeTable->set( id, Scoped{ decl, scope } ); } bool addedDeclConflicts( AggregateDecl *existing, AggregateDecl *added ) { if ( ! existing->body ) { return false; } else if ( added->body ) { SemanticError( added, "redeclaration of " ); } // if return true; } void Indexer::addStruct( const std::string &id ) { addStruct( new StructDecl( id ) ); } void Indexer::addStruct( StructDecl *decl ) { const std::string &id = decl->name; if ( ! structTable ) { structTable = StructTable::new_ptr(); } else { auto existing = structTable->find( id ); if ( existing != structTable->end() && existing->second.scope == scope && addedDeclConflicts( existing->second.decl, decl ) ) return; } structTable = structTable->set( id, Scoped{ decl, scope } ); } void Indexer::addEnum( EnumDecl *decl ) { const std::string &id = decl->name; if ( ! enumTable ) { enumTable = EnumTable::new_ptr(); } else { auto existing = enumTable->find( id ); if ( existing != enumTable->end() && existing->second.scope == scope && addedDeclConflicts( existing->second.decl, decl ) ) return; } enumTable = enumTable->set( id, Scoped{ decl, scope } ); } void Indexer::addUnion( const std::string &id ) { addUnion( new UnionDecl( id ) ); } void Indexer::addUnion( UnionDecl *decl ) { const std::string &id = decl->name; if ( ! unionTable ) { unionTable = UnionTable::new_ptr(); } else { auto existing = unionTable->find( id ); if ( existing != unionTable->end() && existing->second.scope == scope && addedDeclConflicts( existing->second.decl, decl ) ) return; } unionTable = unionTable->set( id, Scoped{ decl, scope } ); } void Indexer::addTrait( TraitDecl *decl ) { const std::string &id = decl->name; if ( ! traitTable ) { traitTable = TraitTable::new_ptr(); } else { auto existing = traitTable->find( id ); if ( existing != traitTable->end() && existing->second.scope == scope && addedDeclConflicts( existing->second.decl, decl ) ) return; } traitTable = traitTable->set( id, Scoped{ decl, scope } ); } void Indexer::addMembers( AggregateDecl * aggr, Expression * expr, OnConflict handleConflicts ) { for ( Declaration * decl : aggr->members ) { if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( decl ) ) { addId( dwt, handleConflicts, expr ); if ( dwt->name == "" ) { Type * t = dwt->get_type()->stripReferences(); if ( dynamic_cast( t ) || dynamic_cast( t ) ) { Expression * base = expr->clone(); ResolvExpr::Cost cost = ResolvExpr::Cost::zero; // xxx - carry this cost into the indexer as a base cost? ResolvExpr::referenceToRvalueConversion( base, cost ); addMembers( t->getAggr(), new MemberExpr( dwt, base ), handleConflicts ); } } } } } void Indexer::addWith( std::list< Expression * > & withExprs, BaseSyntaxNode * withStmt ) { for ( Expression * expr : withExprs ) { if ( expr->result ) { AggregateDecl * aggr = expr->result->stripReferences()->getAggr(); assertf( aggr, "WithStmt expr has non-aggregate type: %s", toString( expr->result ).c_str() ); addMembers( aggr, expr, OnConflict::deleteWith( withStmt ) ); } } } void Indexer::addIds( const std::list< DeclarationWithType * > & decls ) { for ( auto d : decls ) { addId( d ); } } void Indexer::addTypes( const std::list< TypeDecl * > & tds ) { for ( auto td : tds ) { addType( td ); addIds( td->assertions ); } } void Indexer::addFunctionType( FunctionType * ftype ) { addTypes( ftype->forall ); addIds( ftype->returnVals ); addIds( ftype->parameters ); } // void Indexer::print( std::ostream &os, int indent ) const { // using std::cerr; // if ( tables ) { // os << "--- scope " << tables->scope << " ---" << std::endl; // os << "===idTable===" << std::endl; // dump( tables->idTable, os ); // os << "===typeTable===" << std::endl; // dump( tables->typeTable, os ); // os << "===structTable===" << std::endl; // dump( tables->structTable, os ); // os << "===enumTable===" << std::endl; // dump( tables->enumTable, os ); // os << "===unionTable===" << std::endl; // dump( tables->unionTable, os ); // os << "===contextTable===" << std::endl; // dump( tables->traitTable, os ); // tables->base.print( os, indent ); // } else { // os << "--- end ---" << std::endl; // } // } Expression * Indexer::IdData::combine( ResolvExpr::Cost & cost ) const { Expression * ret = nullptr; if ( baseExpr ) { Expression * base = baseExpr->clone(); ResolvExpr::referenceToRvalueConversion( base, cost ); ret = new MemberExpr( id, base ); // xxx - this introduces hidden environments, for now remove them. // std::swap( base->env, ret->env ); delete base->env; base->env = nullptr; } else { ret = new VariableExpr( id ); } if ( deleteStmt ) ret = new DeletedExpr( ret, deleteStmt ); return ret; } } // namespace SymTab // Local Variables: // // tab-width: 4 // // mode: c++ // // compile-command: "make install" // // End: //