| /********************************************************************** |
| * Copyright (c) 2002,2003 Rational Software Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v0.5 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v05. html |
| * |
| * Contributors: |
| * Rational Software - Initial API and implementation |
| * |
| ***********************************************************************/ |
| |
| |
| package org.eclipse.cdt.internal.core.parser.pst; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.cdt.core.parser.Enum; |
| import org.eclipse.cdt.core.parser.ParserLanguage; |
| import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; |
| import org.eclipse.cdt.core.parser.ast.IASTMember; |
| import org.eclipse.cdt.core.parser.ast.IASTNode; |
| import org.eclipse.cdt.internal.core.parser.pst.IDerivableContainerSymbol.IParentSymbol; |
| import org.eclipse.cdt.internal.core.parser.pst.TypeInfo.PtrOp; |
| |
| /** |
| * @author aniefer |
| */ |
| |
| public class ParserSymbolTable { |
| |
| public static final String EMPTY_NAME = ""; //$NON-NLS-1$ |
| public static final String THIS = "this"; //$NON-NLS-1$ |
| |
| /** |
| * Constructor for ParserSymbolTable. |
| */ |
| public ParserSymbolTable( ParserLanguage language ) { |
| super(); |
| _compilationUnit = newContainerSymbol( EMPTY_NAME, TypeInfo.t_namespace ); |
| _language = language; |
| } |
| |
| public IContainerSymbol getCompilationUnit(){ |
| return _compilationUnit; |
| } |
| |
| public IContainerSymbol newContainerSymbol( String name ){ |
| return new ContainerSymbol( this, name ); |
| } |
| public IContainerSymbol newContainerSymbol( String name, TypeInfo.eType type ){ |
| return new ContainerSymbol( this, name, type ); |
| } |
| |
| public ISymbol newSymbol( String name ){ |
| return new BasicSymbol( this, name ); |
| } |
| public ISymbol newSymbol( String name, TypeInfo.eType type ){ |
| return new BasicSymbol( this, name, type ); |
| } |
| |
| public IDerivableContainerSymbol newDerivableContainerSymbol( String name ){ |
| return new DerivableContainerSymbol( this, name ); |
| } |
| public IDerivableContainerSymbol newDerivableContainerSymbol( String name, TypeInfo.eType type ){ |
| return new DerivableContainerSymbol( this, name, type ); |
| } |
| public IParameterizedSymbol newParameterizedSymbol( String name ){ |
| return new ParameterizedSymbol( this, name ); |
| } |
| public IParameterizedSymbol newParameterizedSymbol( String name, TypeInfo.eType type ){ |
| return new ParameterizedSymbol( this, name, type ); |
| } |
| public ISpecializedSymbol newSpecializedSymbol( String name ){ |
| return new SpecializedSymbol( this, name ); |
| } |
| // public ISpecializedSymbol newSpecializedSymbol( String name, TypeInfo.eType type ){ |
| // return new Declaration( this, name, type ); |
| // } |
| /** |
| * Lookup the name from LookupData starting in the inDeclaration |
| * @param data |
| * @param inDeclaration |
| * @return Declaration |
| * @throws ParserSymbolTableException |
| */ |
| static protected void lookup( LookupData data, IContainerSymbol inSymbol ) throws ParserSymbolTableException |
| { |
| // if( data.type != TypeInfo.t_any && data.type.compareTo(TypeInfo.t_class) < 0 && data.upperType.compareTo(TypeInfo.t_union) > 0 ){ |
| // throw new ParserSymbolTableException( ParserSymbolTableException.r_BadTypeInfo ); |
| // } |
| |
| //handle namespace aliases |
| if( inSymbol.isType( TypeInfo.t_namespace ) ){ |
| ISymbol symbol = inSymbol.getTypeSymbol(); |
| if( symbol != null && symbol.isType( TypeInfo.t_namespace ) ){ |
| inSymbol = (IContainerSymbol) symbol; |
| } |
| } |
| |
| ISymbol symbol = null; //the return value |
| LinkedList transitives = new LinkedList(); //list of transitive using directives |
| |
| //if this name define in this scope? |
| Map map = null; |
| if( !data.usingDirectivesOnly ){ |
| map = lookupInContained( data, inSymbol ); |
| if( data.foundItems == null || data.foundItems.isEmpty() ){ |
| data.foundItems = map; |
| } else { |
| mergeResults( data, data.foundItems, map ); |
| } |
| } |
| |
| if( inSymbol.getSymbolTable().getLanguage() == ParserLanguage.CPP && |
| !data.ignoreUsingDirectives ) |
| { |
| //check nominated namespaces |
| //the transitives list is populated in LookupInNominated, and then |
| //processed in ProcessDirectives |
| |
| data.visited.clear(); //each namesapce is searched at most once, so keep track |
| |
| lookupInNominated( data, inSymbol, transitives ); |
| |
| //if we are doing a qualified lookup, only process using directives if |
| //we haven't found the name yet (and if we aren't ignoring them). |
| if( !data.qualified || data.foundItems == null || data.foundItems.isEmpty() ){ |
| processDirectives( inSymbol, data, transitives ); |
| |
| if( inSymbol.hasUsingDirectives() ){ |
| processDirectives( inSymbol, data, inSymbol.getUsingDirectives() ); |
| } |
| |
| while( data.usingDirectives != null && data.usingDirectives.get( inSymbol ) != null ){ |
| transitives.clear(); |
| |
| lookupInNominated( data, inSymbol, transitives ); |
| |
| if( !data.qualified || data.foundItems == null ){ |
| processDirectives( inSymbol, data, transitives ); |
| } |
| } |
| } |
| } |
| |
| if( data.mode == LookupMode.NORMAL && ( !data.foundItems.isEmpty() || data.stopAt == inSymbol ) ){ |
| return; |
| } |
| |
| if( !data.usingDirectivesOnly && inSymbol instanceof IDerivableContainerSymbol ){ |
| //if we still havn't found it, check any parents we have |
| data.visited.clear(); //each virtual base class is searched at most once |
| map = lookupInParents( data, (IDerivableContainerSymbol)inSymbol ); |
| |
| if( data.foundItems == null || data.foundItems.isEmpty() ){ |
| data.foundItems = map; |
| } else { |
| mergeInheritedResults( data.foundItems, map ); |
| } |
| } |
| |
| //if still not found, check our containing scope. |
| if( ( data.foundItems == null || data.foundItems.isEmpty() || data.mode == LookupMode.PREFIX ) |
| && inSymbol.getContainingSymbol() != null ) |
| { |
| if( data.qualified ){ |
| if( data.usingDirectives != null && !data.usingDirectives.isEmpty() ){ |
| data.usingDirectivesOnly = true; |
| lookup( data, inSymbol.getContainingSymbol() ); |
| |
| } |
| } else { |
| lookup( data, inSymbol.getContainingSymbol() ); |
| } |
| |
| } |
| |
| return; |
| } |
| |
| /** |
| * function LookupInNominated |
| * @param data |
| * @param transitiveDirectives |
| * @return List |
| * |
| * for qualified: |
| * 3.4.3.2-2 "let S be the set of all declarations of m in X |
| * and in the transitive closure of all namespaces nominated by using- |
| * directives in X and its used namespaces, except that using-directives are |
| * ignored in any namespace, including X, directly containing one or more |
| * declarations of m." |
| * |
| * for unqualified: |
| * 7.3.4-2 The using-directive is transitive: if a scope contains a using |
| * directive that nominates a second namespace that itself contains using- |
| * directives, the effect is as if the using-directives from the second |
| * namespace also appeared in the first. |
| */ |
| static private void lookupInNominated( LookupData data, IContainerSymbol symbol, LinkedList transitiveDirectives ) throws ParserSymbolTableException{ |
| //if the data.usingDirectives is empty, there is nothing to do. |
| if( data.usingDirectives == null ){ |
| return; |
| } |
| |
| //local variables |
| LinkedList directives = null; //using directives association with declaration |
| Iterator iter = null; |
| IContainerSymbol temp = null; |
| |
| boolean foundSomething = false; |
| int size = 0; |
| |
| directives = (LinkedList) data.usingDirectives.remove( symbol ); |
| |
| if( directives == null ){ |
| return; |
| } |
| |
| iter = directives.iterator(); |
| size = directives.size(); |
| for( int i = size; i > 0; i-- ){ |
| temp = (IContainerSymbol) iter.next(); |
| |
| //namespaces are searched at most once |
| if( !data.visited.contains( temp ) ){ |
| data.visited.add( temp ); |
| |
| Map map = lookupInContained( data, temp ); |
| foundSomething = !map.isEmpty(); |
| mergeResults( data, data.foundItems, map ); |
| |
| //only consider the transitive using directives if we are an unqualified |
| //lookup, or we didn't find the name in decl |
| if( (!data.qualified || !foundSomething || data.mode == LookupMode.PREFIX ) && temp.getUsingDirectives() != null ){ |
| //name wasn't found, add transitive using directives for later consideration |
| transitiveDirectives.addAll( temp.getUsingDirectives() ); |
| } |
| } |
| } |
| |
| return; |
| } |
| |
| /** |
| * @param map |
| * @param map2 |
| */ |
| private static void mergeResults( LookupData data, Map resultMap, Map map ) throws ParserSymbolTableException { |
| if( resultMap == null || map == null || map.isEmpty() ){ |
| return; |
| } |
| |
| Iterator keyIterator = map.keySet().iterator(); |
| Object key = null; |
| while( keyIterator.hasNext() ){ |
| key = keyIterator.next(); |
| if( resultMap.containsKey( key ) ){ |
| List list = new LinkedList(); |
| Object obj = resultMap.get( key ); |
| |
| if ( obj instanceof List ) list.addAll( (List) obj ); |
| else list.add( obj ); |
| |
| obj = map.get( key ); |
| |
| if( obj instanceof List ) list.addAll( (List) obj ); |
| else list.add( obj ); |
| |
| resultMap.put( key, collectSymbol( data, list ) ); |
| } else { |
| resultMap.put( key, map.get( key ) ); |
| } |
| } |
| } |
| |
| /** |
| * function LookupInContained |
| * @param data |
| * @return List |
| * |
| * Look for data.name in our collection _containedDeclarations |
| */ |
| protected static Map lookupInContained( LookupData data, IContainerSymbol lookIn ) throws ParserSymbolTableException{ |
| Map found = new LinkedHashMap(); |
| |
| boolean foundSomething = false; |
| ISymbol temp = null; |
| Object obj = null; |
| |
| if( data.associated != null ){ |
| //we are looking in lookIn, remove it from the associated scopes list |
| data.associated.remove( lookIn ); |
| } |
| |
| Map declarations = lookIn.getContainedSymbols(); |
| |
| Iterator iterator = ( data.mode == LookupMode.PREFIX ) ? declarations.keySet().iterator() : null; |
| String name = ( iterator != null && iterator.hasNext() ) ? (String) iterator.next() : data.name; |
| |
| while( name != null ) { |
| if( nameMatches( data, name ) ){ |
| obj = ( declarations != null ) ? declarations.get( name ) : null; |
| |
| obj = collectSymbol( data, obj ); |
| |
| if( obj != null ) |
| found.put( name, obj ); |
| } |
| |
| if( iterator != null && iterator.hasNext() ){ |
| name = (String) iterator.next(); |
| } else { |
| name = null; |
| } |
| } |
| |
| if( !found.isEmpty() && data.mode == LookupMode.NORMAL ){ |
| return found; |
| } |
| |
| if( lookIn instanceof IParameterizedSymbol ){ |
| Map parameters = ((IParameterizedSymbol)lookIn).getParameterMap(); |
| if( parameters != null ){ |
| iterator = ( data.mode == LookupMode.PREFIX ) ? parameters.keySet().iterator() : null; |
| name = ( iterator != null && iterator.hasNext() ) ? (String) iterator.next() : data.name; |
| while( name != null ){ |
| if( nameMatches( data, name ) ){ |
| obj = parameters.get( name ); |
| obj = collectSymbol( data, obj ); |
| if( obj != null ){ |
| found.put( name, obj ); |
| } |
| } |
| if( iterator != null && iterator.hasNext() ){ |
| name = (String) iterator.next(); |
| } else { |
| name = null; |
| } |
| } |
| |
| } |
| } |
| |
| return found; |
| } |
| |
| private static boolean nameMatches( LookupData data, String name ){ |
| if( data.mode == LookupMode.PREFIX ){ |
| return name.regionMatches( true, 0, data.name, 0, data.name.length() ); |
| } else { |
| return name.equals( data.name ); |
| } |
| } |
| private static boolean checkType( LookupData data, ISymbol symbol ) { //, TypeInfo.eType type, TypeInfo.eType upperType ){ |
| if( data.filter == null ){ |
| return true; |
| } |
| |
| if( data.templateInstance != null && symbol.isTemplateMember() ){ |
| if( symbol.isType( TypeInfo.t_type ) ){ |
| symbol = symbol.getTypeSymbol(); |
| } |
| if( symbol.isType( TypeInfo.t_undef ) && symbol.getContainingSymbol().isType( TypeInfo.t_template ) ){ |
| TypeInfo info = (TypeInfo) data.templateInstance.getArgumentMap().get( symbol ); |
| return data.filter.shouldAccept( symbol, info ); |
| } |
| } |
| return data.filter.shouldAccept( symbol ); |
| } |
| |
| private static Object collectSymbol(LookupData data, Object object ) throws ParserSymbolTableException { |
| if( object == null ){ |
| return null; |
| } |
| |
| ISymbol foundSymbol = null; |
| |
| Iterator iter = ( object instanceof List ) ? ((List)object).iterator() : null; |
| ISymbol symbol = ( iter != null ) ? (ISymbol) iter.next() : (ISymbol) object; |
| |
| Set functionSet = new HashSet(); |
| ISymbol obj = null; |
| IContainerSymbol cls = null; |
| |
| while( symbol != null ){ |
| if( !symbol.getIsInvisible() && checkType( data, symbol ) ){//, data.type, data.upperType ) ){ |
| if( symbol.isTemplateMember() && data.templateInstance != null ) |
| foundSymbol = new TemplateInstance( symbol.getSymbolTable(), symbol, data.templateInstance.getArgumentMap() ); |
| else |
| foundSymbol = symbol; |
| |
| if( foundSymbol.isType( TypeInfo.t_function ) ){ |
| if( foundSymbol.isForwardDeclaration() && foundSymbol.getTypeSymbol() != null ){ |
| foundSymbol = foundSymbol.getTypeSymbol(); |
| } |
| |
| functionSet.add( foundSymbol ); |
| } else { |
| //if this is a class-name, other stuff hides it |
| if( foundSymbol.isType( TypeInfo.t_class, TypeInfo.t_enumeration ) ){ |
| if( cls == null ){ |
| cls = (IContainerSymbol) foundSymbol; |
| } else { |
| if( cls.getTypeInfo().isForwardDeclaration() && cls.getTypeSymbol() == foundSymbol ){ |
| //cls is a forward declaration of decl, we want decl. |
| cls = (IContainerSymbol) foundSymbol; |
| } else if( foundSymbol.getTypeInfo().isForwardDeclaration() && foundSymbol.getTypeSymbol() == cls ){ |
| //decl is a forward declaration of cls, we already have what we want (cls) |
| } else { |
| if( data.mode == LookupMode.PREFIX ){ |
| if( data.ambiguities == null ){ |
| data.ambiguities = new HashSet(); |
| } |
| data.ambiguities.add( foundSymbol.getName() ); |
| } else { |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_Ambiguous ); |
| } |
| } |
| } |
| } else { |
| //an object, can only have one of these |
| if( obj == null ){ |
| obj = foundSymbol; |
| } else { |
| if( data.mode == LookupMode.PREFIX ){ |
| if( data.ambiguities == null ){ |
| data.ambiguities = new HashSet(); |
| } |
| data.ambiguities.add( foundSymbol.getName() ); |
| } else { |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_Ambiguous ); |
| } |
| } |
| } |
| } |
| } |
| |
| if( iter != null ){ |
| symbol = iter.hasNext() ? (ISymbol) iter.next() : null; |
| } else { |
| symbol = null; |
| } |
| } |
| |
| int numFunctions = functionSet.size(); |
| |
| boolean ambiguous = false; |
| |
| if( cls != null ){ |
| //the class is only hidden by other stuff if they are from the same scope |
| if( obj != null && cls.getContainingSymbol() != obj.getContainingSymbol()){ |
| ambiguous = true; |
| } |
| if( !functionSet.isEmpty() ){ |
| Iterator fnIter = functionSet.iterator(); |
| IParameterizedSymbol fn = null; |
| for( int i = numFunctions; i > 0; i-- ){ |
| fn = (IParameterizedSymbol) fnIter.next(); |
| if( cls.getContainingSymbol()!= fn.getContainingSymbol()){ |
| ambiguous = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| if( obj != null && !ambiguous ){ |
| if( numFunctions > 0 ){ |
| ambiguous = true; |
| } else { |
| return obj; |
| } |
| } else if( numFunctions > 0 ) { |
| return new LinkedList( functionSet ); |
| } |
| |
| if( ambiguous ){ |
| if( data.mode == LookupMode.PREFIX ){ |
| if( data.ambiguities == null ){ |
| data.ambiguities = new HashSet(); |
| } |
| data.ambiguities.add( foundSymbol.getName() ); |
| } else { |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_Ambiguous ); |
| } |
| } |
| |
| return cls; |
| } |
| /** |
| * |
| * @param data |
| * @param lookIn |
| * @return Declaration |
| * @throws ParserSymbolTableException |
| */ |
| private static Map lookupInParents( LookupData data, ISymbol lookIn ) throws ParserSymbolTableException{ |
| IDerivableContainerSymbol container = null; |
| /*if( lookIn instanceof TemplateInstance ){ |
| |
| } else*/ |
| if( lookIn instanceof IDerivableContainerSymbol ){ |
| container = (IDerivableContainerSymbol) lookIn; |
| } else{ |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_InternalError ); |
| } |
| |
| List scopes = container.getParents(); |
| |
| Map temp = null; |
| Map symbol = null; |
| Map inherited = null; |
| |
| Iterator iterator = null; |
| IDerivableContainerSymbol.IParentSymbol wrapper = null; |
| |
| if( scopes == null ) |
| return null; |
| |
| //use data to detect circular inheritance |
| if( data.inheritanceChain == null ) |
| data.inheritanceChain = new HashSet(); |
| |
| data.inheritanceChain.add( container ); |
| |
| iterator = scopes.iterator(); |
| |
| int size = scopes.size(); |
| |
| for( int i = size; i > 0; i-- ) |
| { |
| wrapper = (IDerivableContainerSymbol.IParentSymbol) iterator.next(); |
| ISymbol parent = wrapper.getParent(); |
| if( parent.isType( TypeInfo.t_undef ) && parent.getContainingSymbol().isType( TypeInfo.t_template ) ){ |
| TypeInfo info = (TypeInfo) data.templateInstance.getArgumentMap().get( parent ); |
| if( info.getTypeSymbol() instanceof IDerivableContainerSymbol ){ |
| parent = (IDerivableContainerSymbol) info.getTypeSymbol(); |
| } else { |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_BadTemplate ); |
| } |
| } |
| if( !wrapper.isVirtual() || !data.visited.contains( parent ) ){ |
| if( wrapper.isVirtual() ){ |
| data.visited.add( parent ); |
| } |
| |
| //if the inheritanceChain already contains the parent, then that |
| //is circular inheritance |
| if( ! data.inheritanceChain.contains( parent ) ){ |
| //is this name define in this scope? |
| if( parent instanceof TemplateInstance ){ |
| ISymbol tempInstance = data.templateInstance; |
| data.templateInstance = (TemplateInstance) parent; |
| ISymbol instance = ((TemplateInstance)parent).getInstantiatedSymbol(); |
| if( instance instanceof IContainerSymbol ) |
| temp = lookupInContained( data, (IContainerSymbol)instance ); |
| else |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_BadTemplate ); |
| data.templateInstance = tempInstance; |
| } else if( parent instanceof IDerivableContainerSymbol ){ |
| temp = lookupInContained( data, (IDerivableContainerSymbol) parent ); |
| } else { |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_BadTypeInfo ); |
| } |
| |
| if( temp.isEmpty() || data.mode == LookupMode.PREFIX ){ |
| inherited = lookupInParents( data, parent ); |
| mergeInheritedResults( temp, inherited ); |
| } |
| } else { |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_CircularInheritance ); |
| } |
| } |
| |
| if( temp != null && !temp.isEmpty() ){ |
| if( symbol == null || symbol.isEmpty() ){ |
| symbol = temp; |
| } else if ( temp != null && !temp.isEmpty() ) { |
| Iterator iter = temp.keySet().iterator(); |
| Object key = null; |
| while( iter.hasNext() ){ |
| key = iter.next(); |
| if( symbol.containsKey( key ) ){ |
| ISymbol sym = (ISymbol) symbol.get( key ); |
| if( !checkAmbiguity( sym, temp.get( key ) ) ){ |
| if( data.mode == LookupMode.PREFIX ){ |
| if( data.ambiguities == null ){ |
| data.ambiguities = new HashSet(); |
| } |
| data.ambiguities.add( sym.getName() ); |
| } else { |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_Ambiguous ); |
| } |
| } |
| } else { |
| symbol.put( key, temp.get( key ) ); |
| } |
| } |
| } |
| } else { |
| temp = null; //reset temp for next iteration |
| } |
| } |
| |
| data.inheritanceChain.remove( container ); |
| |
| return symbol; |
| } |
| |
| private static boolean checkAmbiguity( Object obj1, Object obj2 ) throws ParserSymbolTableException{ |
| //it is not ambiguous if they are the same thing and it is static or an enumerator |
| if( obj1 == obj2 ){ |
| |
| Iterator iter = ( obj1 instanceof List ) ? ((List) obj1).iterator() : null; |
| ISymbol symbol = ( iter != null ) ? (ISymbol) iter.next() : ( ISymbol )obj1; |
| while( symbol != null ) { |
| TypeInfo type = ((ISymbol)obj1).getTypeInfo(); |
| if( !type.checkBit( TypeInfo.isStatic ) && !type.isType( TypeInfo.t_enumerator ) ){ |
| return false; |
| } |
| |
| if( iter != null && iter.hasNext() ){ |
| symbol = (ISymbol) iter.next(); |
| } else { |
| symbol = null; |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Symbols in map are added to the resultMap if a symbol with that name does not already exist there |
| * @param resultMap |
| * @param map |
| * @throws ParserSymbolTableException |
| */ |
| private static void mergeInheritedResults( Map resultMap, Map map ) throws ParserSymbolTableException{ |
| if( resultMap == null || map == null || map.isEmpty() ){ |
| return; |
| } |
| |
| Iterator keyIterator = map.keySet().iterator(); |
| Object key = null; |
| while( keyIterator.hasNext() ){ |
| key = keyIterator.next(); |
| if( !resultMap.containsKey( key ) ){ |
| resultMap.put( key, map.get( key ) ); |
| } |
| } |
| } |
| |
| /** |
| * function isValidOverload |
| * @param origDecl |
| * @param newDecl |
| * @return boolean |
| * |
| * 3.3.7 "A class name or enumeration name can be hidden by the name of an |
| * object, function or enumerator declared in the same scope" |
| * |
| * 3.4-1 "Name lookup may associate more than one declaration with a name if |
| * it finds the name to be a function name" |
| */ |
| protected static boolean isValidOverload( ISymbol origSymbol, ISymbol newSymbol ){ |
| TypeInfo.eType origType = origSymbol.getType(); |
| TypeInfo.eType newType = newSymbol.getType(); |
| |
| //handle forward decls |
| if( origSymbol.getTypeInfo().isForwardDeclaration() ){ |
| if( origSymbol.getTypeSymbol() == newSymbol ) |
| return true; |
| |
| //friend class declarations |
| if( origSymbol.getIsInvisible() && origSymbol.isType( newSymbol.getType() ) ){ |
| origSymbol.getTypeInfo().setTypeSymbol( newSymbol ); |
| return true; |
| } |
| } |
| |
| if( (origType.compareTo(TypeInfo.t_class) >= 0 && origType.compareTo(TypeInfo.t_enumeration) <= 0) && //class name or enumeration ... |
| ( newType == TypeInfo.t_type || (newType.compareTo( TypeInfo.t_function ) >= 0 /*&& newType <= TypeInfo.typeMask*/) ) ){ |
| |
| return true; |
| } |
| //if the origtype is not a class-name or enumeration name, then the only other |
| //allowable thing is if they are both functions. |
| if( origSymbol instanceof IParameterizedSymbol && newSymbol instanceof IParameterizedSymbol ) |
| return isValidFunctionOverload( (IParameterizedSymbol) origSymbol, (IParameterizedSymbol) newSymbol ); |
| else |
| return false; |
| } |
| |
| protected static boolean isValidOverload( List origList, ISymbol newSymbol ){ |
| if( origList.size() == 1 ){ |
| return isValidOverload( (ISymbol)origList.iterator().next(), newSymbol ); |
| } else if ( origList.size() > 1 ){ |
| |
| //the first thing can be a class-name or enumeration name, but the rest |
| //must be functions. So make sure the newDecl is a function before even |
| //considering the list |
| if( newSymbol.getType() != TypeInfo.t_function && newSymbol.getType() != TypeInfo.t_constructor ){ |
| return false; |
| } |
| |
| Iterator iter = origList.iterator(); |
| ISymbol symbol = (ISymbol) iter.next(); |
| boolean valid = isValidOverload( symbol, newSymbol );//( (symbol.getType().compareTo( TypeInfo.t_class ) >= 0 ) && (symbol.getType().compareTo( TypeInfo.t_enumeration ) <= 0 ) ); |
| |
| if( !valid && (symbol instanceof IParameterizedSymbol) ) |
| valid = isValidFunctionOverload( (IParameterizedSymbol)symbol, (IParameterizedSymbol)newSymbol ); |
| |
| while( valid && iter.hasNext() ){ |
| symbol = (ISymbol) iter.next(); |
| valid = ( symbol instanceof IParameterizedSymbol) && isValidFunctionOverload( (IParameterizedSymbol)symbol, (IParameterizedSymbol)newSymbol ); |
| } |
| |
| return valid; |
| } |
| |
| //empty list, return true |
| return true; |
| } |
| |
| private static boolean isValidFunctionOverload( IParameterizedSymbol origSymbol, IParameterizedSymbol newSymbol ){ |
| if( ( !origSymbol.isType( TypeInfo.t_function ) && !origSymbol.isType( TypeInfo.t_constructor ) ) || |
| ( ! newSymbol.isType( TypeInfo.t_function ) && ! newSymbol.isType( TypeInfo.t_constructor ) ) ){ |
| return false; |
| } |
| |
| //handle forward decls |
| if( origSymbol.getTypeInfo().isForwardDeclaration() && |
| origSymbol.getTypeSymbol() == newSymbol ) |
| { |
| return true; |
| } |
| if( origSymbol.hasSameParameters( newSymbol ) ){ |
| //functions with the same name and same parameter types cannot be overloaded if any of them |
| //is static |
| if( origSymbol.getTypeInfo().checkBit( TypeInfo.isStatic ) || newSymbol.getTypeInfo().checkBit( TypeInfo.isStatic ) ){ |
| return false; |
| } |
| |
| //if none of them are static, then the function can be overloaded if they differ in the type |
| //of their implicit object parameter. |
| if( origSymbol.compareCVQualifiersTo( newSymbol ) != 0 ){ |
| return true; |
| } |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * |
| * @param data |
| * @return Declaration |
| * @throws ParserSymbolTableException |
| * |
| * Resolve the foundItems set down to one declaration and return that |
| * declaration. |
| * If we successfully resolve, then the data.foundItems list will be |
| * cleared. If however, we were not able to completely resolve the set, |
| * then the data.foundItems set will be left with those items that |
| * survived the partial resolution and we will return null. (currently, |
| * this case applies to when we have overloaded functions and no parameter |
| * information) |
| * |
| * NOTE: data.parameters == null means there is no parameter information at |
| * all, when looking for functions with no parameters, an empty list must be |
| * provided in data.parameters. |
| */ |
| static protected ISymbol resolveAmbiguities( LookupData data ) throws ParserSymbolTableException{ |
| ISymbol decl = null; |
| ISymbol obj = null; |
| IContainerSymbol cls = null; |
| |
| if( data.foundItems == null || data.foundItems.isEmpty() || data.mode == LookupMode.PREFIX ){ |
| return null; |
| } |
| |
| int size = data.foundItems.size(); |
| //Iterator iter = data.foundItems.iterator(); |
| |
| Object object = data.foundItems.get( data.name ); |
| |
| LinkedList functionList = new LinkedList(); |
| |
| if( object instanceof List ){ |
| functionList.addAll( (List) object ); |
| } else { |
| ISymbol symbol = (ISymbol) object; |
| if( symbol.isType( TypeInfo.t_function ) ){ |
| functionList.add( symbol ); |
| } else { |
| return symbol; |
| } |
| } |
| |
| if( data.parameters == null ){ |
| //we have no parameter information, if we only have one function, return |
| //that, otherwise we can't decide between them |
| if( functionList.size() == 1){ |
| return (ISymbol) functionList.getFirst(); |
| } else { |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_UnableToResolveFunction ); |
| } |
| } else { |
| return resolveFunction( data, functionList ); |
| } |
| } |
| |
| static protected IParameterizedSymbol resolveFunction( LookupData data, List functions ) throws ParserSymbolTableException{ |
| if( functions == null ){ |
| return null; |
| } |
| |
| reduceToViable( data, functions ); |
| |
| int numSourceParams = ( data.parameters == null ) ? 0 : data.parameters.size(); |
| int numFns = functions.size(); |
| |
| if( numSourceParams == 0 ){ |
| //no parameters |
| //if there is only 1 viable function, return it, if more than one, its ambiguous |
| if( numFns == 0 ){ |
| return null; |
| } else if ( numFns == 1 ){ |
| return (IParameterizedSymbol)functions.iterator().next(); |
| } else if ( numFns == 2 ){ |
| Iterator iter = functions.iterator(); |
| while( iter.hasNext() ){ |
| IParameterizedSymbol fn = (IParameterizedSymbol) iter.next(); |
| if( fn.getTypeInfo().isForwardDeclaration() && fn.getTypeSymbol() != null ){ |
| if( functions.contains( fn.getTypeSymbol() ) ){ |
| return (IParameterizedSymbol) fn.getTypeSymbol(); |
| } |
| } |
| } |
| } |
| |
| if( data.parameters == null ) |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_Ambiguous ); |
| } |
| |
| IParameterizedSymbol bestFn = null; //the best function |
| IParameterizedSymbol currFn = null; //the function currently under consideration |
| Cost [] bestFnCost = null; //the cost of the best function |
| Cost [] currFnCost = null; //the cost for the current function |
| |
| Iterator iterFns = functions.iterator(); |
| Iterator sourceParams = null; |
| Iterator targetParams = null; |
| |
| int numTargetParams = 0; |
| int comparison; |
| Cost cost = null; |
| Cost temp = null; |
| |
| TypeInfo source = null; |
| TypeInfo target = null; |
| |
| boolean hasWorse = false; |
| boolean hasBetter = false; |
| boolean ambiguous = false; |
| boolean currHasAmbiguousParam = false; |
| boolean bestHasAmbiguousParam = false; |
| |
| List parameters = null; |
| |
| if( numSourceParams == 0 ){ |
| parameters = new LinkedList(); |
| parameters.add( new TypeInfo( TypeInfo.t_void, 0, null ) ); |
| numSourceParams = 1; |
| } else { |
| parameters = data.parameters; |
| } |
| |
| for( int i = numFns; i > 0; i-- ){ |
| currFn = (IParameterizedSymbol) iterFns.next(); |
| |
| if( bestFn != null ){ |
| if( bestFn.isForwardDeclaration() && bestFn.getTypeSymbol() == currFn ){ |
| bestFn = currFn; |
| continue; |
| } else if( currFn.isForwardDeclaration() && currFn.getTypeSymbol() == bestFn ){ |
| continue; |
| } |
| } |
| |
| sourceParams = parameters.iterator(); |
| |
| List parameterList = null; |
| if( currFn.getParameterList().isEmpty() && !currFn.hasVariableArgs() ){ |
| //the only way we get here and have no parameters, is if we are looking |
| //for a function that takes void parameters ie f( void ) |
| parameterList = new LinkedList(); |
| parameterList.add( currFn.getSymbolTable().newSymbol( "", TypeInfo.t_void ) ); |
| } else { |
| parameterList = currFn.getParameterList(); |
| } |
| |
| targetParams = parameterList.iterator(); |
| numTargetParams = parameterList.size(); |
| |
| if( currFnCost == null ){ |
| currFnCost = new Cost [ numSourceParams ]; |
| } |
| |
| comparison = 0; |
| boolean varArgs = false; |
| |
| for( int j = 0; j < numSourceParams; j++ ){ |
| source = (TypeInfo) sourceParams.next(); |
| |
| if( targetParams.hasNext() ) |
| target = ((ISymbol)targetParams.next()).getTypeInfo(); |
| else |
| varArgs = true; |
| |
| if( varArgs ){ |
| cost = new Cost( source, null ); |
| cost.rank = Cost.ELLIPSIS_CONVERSION; |
| } else if ( target.getHasDefault() && source.isType( TypeInfo.t_void ) && !source.hasPtrOperators() ){ |
| //source is just void, ie no parameter, if target had a default, then use that |
| cost = new Cost( source, target ); |
| cost.rank = Cost.IDENTITY_RANK; |
| } else if( source.equals( target ) ){ |
| cost = new Cost( source, target ); |
| cost.rank = Cost.IDENTITY_RANK; //exact match, no cost |
| } else { |
| |
| cost = checkStandardConversionSequence( source, target ); |
| |
| //12.3-4 At most one user-defined conversion is implicitly applied to |
| //a single value. (also prevents infinite loop) |
| if( cost.rank == Cost.NO_MATCH_RANK && !data.forUserDefinedConversion ){ |
| temp = checkUserDefinedConversionSequence( source, target ); |
| if( temp != null ){ |
| cost = temp; |
| } |
| } |
| } |
| |
| currFnCost[ j ] = cost; |
| } |
| |
| |
| hasWorse = false; |
| hasBetter = false; |
| //In order for this function to be better than the previous best, it must |
| //have at least one parameter match that is better that the corresponding |
| //match for the other function, and none that are worse. |
| for( int j = 0; j < numSourceParams; j++ ){ |
| if( currFnCost[ j ].rank < 0 ){ |
| hasWorse = true; |
| hasBetter = false; |
| break; |
| } |
| |
| //an ambiguity in the user defined conversion sequence is only a problem |
| //if this function turns out to be the best. |
| currHasAmbiguousParam = ( currFnCost[ j ].userDefined == 1 ); |
| |
| if( bestFnCost != null ){ |
| comparison = currFnCost[ j ].compare( bestFnCost[ j ] ); |
| hasWorse |= ( comparison < 0 ); |
| hasBetter |= ( comparison > 0 ); |
| } else { |
| hasBetter = true; |
| } |
| } |
| |
| //If function has a parameter match that is better than the current best, |
| //and another that is worse (or everything was just as good, neither better nor worse). |
| //then this is an ambiguity (unless we find something better than both later) |
| ambiguous |= ( hasWorse && hasBetter ) || ( !hasWorse && !hasBetter ); |
| |
| if( !hasWorse ){ |
| if( hasBetter ){ |
| //the new best function. |
| ambiguous = false; |
| bestFnCost = currFnCost; |
| bestHasAmbiguousParam = currHasAmbiguousParam; |
| currFnCost = null; |
| bestFn = currFn; |
| } |
| } |
| } |
| |
| if( ambiguous || bestHasAmbiguousParam ){ |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_Ambiguous ); |
| } |
| |
| return bestFn; |
| } |
| |
| static private void reduceToViable( LookupData data, List functions ){ |
| int numParameters = ( data.parameters == null ) ? 0 : data.parameters.size(); |
| int num; |
| |
| //Trim the list down to the set of viable functions |
| IParameterizedSymbol function; |
| Iterator iter = functions.iterator(); |
| while( iter.hasNext() ){ |
| function = (IParameterizedSymbol) iter.next(); |
| num = ( function.getParameterList() == null ) ? 0 : function.getParameterList().size(); |
| |
| //if there are m arguments in the list, all candidate functions having m parameters |
| //are viable |
| if( num == numParameters ){ |
| continue; |
| } |
| //check for void |
| else if( numParameters == 0 && num == 1 ){ |
| ISymbol param = (ISymbol)function.getParameterList().iterator().next(); |
| if( param.isType( TypeInfo.t_void ) ) |
| continue; |
| } |
| else if( numParameters == 1 && num == 0 ){ |
| TypeInfo paramType = (TypeInfo) data.parameters.iterator().next(); |
| if( paramType.isType( TypeInfo.t_void ) ) |
| continue; |
| } |
| |
| //A candidate function having fewer than m parameters is viable only if it has an |
| //ellipsis in its parameter list. |
| if( num < numParameters ){ |
| if( function.hasVariableArgs() ) { |
| continue; |
| } else { |
| //not enough parameters, remove it |
| iter.remove(); |
| } |
| } |
| //a candidate function having more than m parameters is viable only if the (m+1)-st |
| //parameter has a default argument |
| else { |
| ListIterator listIter = function.getParameterList().listIterator( num ); |
| TypeInfo param; |
| for( int i = num; i > ( numParameters - num + 1); i-- ){ |
| param = ((ISymbol)listIter.previous()).getTypeInfo(); |
| if( !param.getHasDefault() ){ |
| iter.remove(); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * function ProcessDirectives |
| * @param Declaration decl |
| * @param LookupData data |
| * @param LinkedList directives |
| * |
| * Go through the directives and for each nominated namespace find the |
| * closest enclosing declaration for that namespace and decl, then add the |
| * nominated namespace to the lookup data for consideration when we reach |
| * the enclosing declaration. |
| */ |
| static private void processDirectives( IContainerSymbol symbol, LookupData data, List directives ){ |
| IContainerSymbol enclosing = null; |
| IContainerSymbol temp = null; |
| |
| int size = directives.size(); |
| Iterator iter = directives.iterator(); |
| |
| for( int i = size; i > 0; i-- ){ |
| temp = (IContainerSymbol) iter.next(); |
| |
| //namespaces are searched at most once |
| if( !data.visited.contains( temp ) ){ |
| enclosing = getClosestEnclosingDeclaration( symbol, temp ); |
| |
| //the data.usingDirectives is a map from enclosing declaration to |
| //a list of namespaces to consider when we reach that enclosing |
| //declaration |
| LinkedList list = (data.usingDirectives == null ) |
| ? null |
| : (LinkedList) data.usingDirectives.get( enclosing ); |
| if ( list == null ){ |
| list = new LinkedList(); |
| list.add( temp ); |
| if( data.usingDirectives == null ){ |
| data.usingDirectives = new HashMap(); |
| } |
| data.usingDirectives.put( enclosing, list ); |
| } else { |
| list.add( temp ); |
| } |
| } |
| } |
| } |
| |
| /** |
| * function getClosestEnclosingDeclaration |
| * @param decl1 |
| * @param decl2 |
| * @return Declaration |
| * |
| * 7.3.4-1 "During unqualified lookup, the names appear as if they were |
| * declared in the nearest enclosing namespace which contains both the |
| * using-directive and the nominated namespace" |
| * |
| * TBD: Consider rewriting this iteratively instead of recursively, for |
| * performance |
| */ |
| static private IContainerSymbol getClosestEnclosingDeclaration( ISymbol symbol1, ISymbol symbol2 ){ |
| if( symbol1 == symbol2 ){ |
| return ( symbol1 instanceof IContainerSymbol ) ? (IContainerSymbol) symbol1 : symbol1.getContainingSymbol(); |
| } |
| |
| if( symbol1.getDepth() == symbol2.getDepth() ){ |
| return getClosestEnclosingDeclaration( symbol1.getContainingSymbol(), symbol2.getContainingSymbol() ); |
| } else if( symbol1.getDepth() > symbol2.getDepth() ) { |
| return getClosestEnclosingDeclaration( symbol1.getContainingSymbol(), symbol2 ); |
| } else { |
| return getClosestEnclosingDeclaration( symbol1, symbol2.getContainingSymbol() ); |
| } |
| } |
| |
| /** |
| * |
| * @param obj |
| * @param base |
| * @return int |
| * figure out if base is a base class of obj, and return the "distance" to |
| * the base class. |
| * ie: |
| * A -> B -> C |
| * the distance from A to B is 1 and from A to C is 2. This distance is used |
| * to rank standard pointer conversions. |
| * |
| * TBD: Consider rewriting iteratively for performance. |
| */ |
| static protected int hasBaseClass( ISymbol obj, ISymbol base ) throws ParserSymbolTableException { |
| return hasBaseClass( obj, base, false ); |
| } |
| |
| static private int hasBaseClass( ISymbol obj, ISymbol base, boolean throwIfNotVisible ) throws ParserSymbolTableException{ |
| if( obj == base ){ |
| return 0; |
| } |
| IDerivableContainerSymbol symbol = null; |
| TemplateInstance instance = null; |
| if( obj instanceof TemplateInstance ){ |
| instance = (TemplateInstance) obj; |
| ISymbol temp = instance.getInstantiatedSymbol(); |
| if( temp instanceof IDerivableContainerSymbol ){ |
| symbol = (IDerivableContainerSymbol) temp; |
| } else { |
| return -1; |
| } |
| } else if( obj instanceof IDerivableContainerSymbol ){ |
| symbol = (IDerivableContainerSymbol) obj; |
| } else { |
| return -1; |
| } |
| |
| if( symbol.hasParents() ){ |
| ISymbol temp = null; |
| IDerivableContainerSymbol parent = null; |
| IDerivableContainerSymbol.IParentSymbol wrapper; |
| |
| Iterator iter = symbol.getParents().iterator(); |
| int size = symbol.getParents().size(); |
| |
| for( int i = size; i > 0; i-- ){ |
| wrapper = (IDerivableContainerSymbol.IParentSymbol) iter.next(); |
| temp = wrapper.getParent(); |
| boolean isVisible = ( wrapper.getVisibility() == ASTAccessVisibility.PUBLIC ); |
| if( temp instanceof TemplateInstance ){ |
| instance = (TemplateInstance) temp; |
| if( instance.getInstantiatedSymbol() instanceof IDerivableContainerSymbol ){ |
| if( instance.getInstantiatedSymbol() == base ){ |
| if( throwIfNotVisible && !isVisible ) |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_BadVisibility ); |
| else |
| return 1; |
| } else { |
| int n = hasBaseClass( instance, base, throwIfNotVisible ); |
| if( n > 0 ){ |
| return n + 1; |
| } |
| } |
| } |
| } else { |
| if( temp.isType( TypeInfo.t_undef ) && temp.getContainingSymbol().isType( TypeInfo.t_template ) ){ |
| if( instance == null ) continue; |
| TypeInfo info = (TypeInfo) instance.getArgumentMap().get( temp ); |
| if( info == null || !info.isType( TypeInfo.t_class, TypeInfo.t_struct ) ){ |
| continue; |
| } |
| parent = (IDerivableContainerSymbol) info.getTypeSymbol(); |
| } |
| else if ( temp instanceof IDerivableContainerSymbol ){ |
| parent = (IDerivableContainerSymbol)temp; |
| } else { |
| continue; |
| } |
| if( parent == base ){ |
| if( throwIfNotVisible && !isVisible ) |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_BadVisibility ); |
| else |
| return 1; |
| } else { |
| int n = hasBaseClass( parent, base, throwIfNotVisible ); |
| if( n > 0 ){ |
| return n + 1; |
| } |
| } |
| } |
| } |
| } |
| |
| return -1; |
| } |
| |
| static protected void getAssociatedScopes( ISymbol symbol, HashSet associated ){ |
| if( symbol == null ){ |
| return; |
| } |
| //if T is a class type, its associated classes are the class itself, |
| //and its direct and indirect base classes. its associated Namespaces are the |
| //namespaces in which its associated classes are defined |
| //if( symbol.getType() == TypeInfo.t_class ){ |
| if( symbol instanceof IDerivableContainerSymbol ){ |
| associated.add( symbol ); |
| associated.add( symbol.getContainingSymbol() ); |
| getBaseClassesAndContainingNamespaces( (IDerivableContainerSymbol) symbol, associated ); |
| } |
| //if T is a union or enumeration type, its associated namespace is the namespace in |
| //which it is defined. if it is a class member, its associated class is the member's |
| //class |
| else if( symbol.getType() == TypeInfo.t_union || symbol.getType() == TypeInfo.t_enumeration ){ |
| associated.add( symbol.getContainingSymbol() ); |
| } |
| } |
| |
| static private void getBaseClassesAndContainingNamespaces( IDerivableContainerSymbol obj, HashSet classes ){ |
| if( obj.getParents() != null ){ |
| if( classes == null ){ |
| return; |
| } |
| |
| Iterator iter = obj.getParents().iterator(); |
| int size = obj.getParents().size(); |
| IDerivableContainerSymbol.IParentSymbol wrapper; |
| ISymbol base; |
| |
| for( int i = size; i > 0; i-- ){ |
| wrapper = (IDerivableContainerSymbol.IParentSymbol) iter.next(); |
| base = wrapper.getParent(); |
| classes.add( base ); |
| if( base.getContainingSymbol().getType() == TypeInfo.t_namespace ){ |
| classes.add( base.getContainingSymbol()); |
| } |
| |
| getBaseClassesAndContainingNamespaces( (IDerivableContainerSymbol) base, classes ); |
| } |
| } |
| } |
| |
| static protected boolean okToAddUsingDeclaration( ISymbol obj, IContainerSymbol context ){ |
| boolean okToAdd = false; |
| |
| //7.3.3-5 A using-declaration shall not name a template-id |
| if( obj.isTemplateMember() && obj.getContainingSymbol().isType( TypeInfo.t_template ) ){ |
| okToAdd = false; |
| } |
| //7.3.3-4 |
| else if( context.isType( TypeInfo.t_class, TypeInfo.t_struct ) ){ |
| IContainerSymbol container = obj.getContainingSymbol(); |
| |
| try{ |
| //a member of a base class |
| if( obj.getContainingSymbol().getType() == context.getType() ){ |
| okToAdd = ( hasBaseClass( (IDerivableContainerSymbol) context, (IDerivableContainerSymbol) container ) > 0 ); |
| } |
| else if ( obj.getContainingSymbol().getType() == TypeInfo.t_union ) { |
| // TODO : must be an _anonymous_ union |
| container = container.getContainingSymbol(); |
| okToAdd = ( container instanceof IDerivableContainerSymbol ) |
| ? ( hasBaseClass( (IDerivableContainerSymbol)context, (IDerivableContainerSymbol) container ) > 0 ) |
| : false; |
| } |
| //an enumerator for an enumeration |
| else if ( obj.getType() == TypeInfo.t_enumerator ){ |
| container = container.getContainingSymbol(); |
| okToAdd = ( container instanceof IDerivableContainerSymbol ) |
| ? ( hasBaseClass( (IDerivableContainerSymbol)context, (IDerivableContainerSymbol) container ) > 0 ) |
| : false; |
| } |
| } catch ( ParserSymbolTableException e ) { |
| //not going to happen since we didn't ask for the visibility exception from hasBaseClass |
| } |
| } else { |
| okToAdd = true; |
| } |
| |
| return okToAdd; |
| } |
| |
| static private Cost lvalue_to_rvalue( TypeInfo source, TypeInfo target ){ |
| |
| //lvalues will have type t_type |
| if( source.isType( TypeInfo.t_type ) ){ |
| source = getFlatTypeInfo( source ); |
| } |
| |
| if( target.isType( TypeInfo.t_type ) ){ |
| ISymbol symbol = target.getTypeSymbol(); |
| if( symbol != null && symbol.isForwardDeclaration() && symbol.getTypeSymbol() != null ){ |
| target = new TypeInfo( target ); |
| target.setType( TypeInfo.t_type ); |
| target.setTypeSymbol( symbol.getTypeSymbol() ); |
| } |
| } |
| |
| Cost cost = new Cost( source, target ); |
| |
| //if either source or target is null here, then there was a problem |
| //with the parameters and we can't match them. |
| if( cost.source == null || cost.target == null ){ |
| return cost; |
| } |
| |
| TypeInfo.PtrOp op = null; |
| |
| if( cost.source.hasPtrOperators() ){ |
| List sourcePtrs = cost.source.getPtrOperators(); |
| Iterator iterator = sourcePtrs.iterator(); |
| TypeInfo.PtrOp ptr = (TypeInfo.PtrOp)iterator.next(); |
| if( ptr.getType() == TypeInfo.PtrOp.t_reference ){ |
| iterator.remove(); |
| } |
| int size = sourcePtrs.size(); |
| Iterator iter = sourcePtrs.iterator(); |
| |
| for( int i = size; i > 0; i-- ){ |
| op = (TypeInfo.PtrOp) iter.next(); |
| if( op.getType() == TypeInfo.PtrOp.t_array ){ |
| op.setType( TypeInfo.PtrOp.t_pointer ); |
| } |
| } |
| } |
| |
| if( cost.target.hasPtrOperators() ){ |
| List targetPtrs = cost.target.getPtrOperators(); |
| ListIterator iterator = targetPtrs.listIterator(); |
| TypeInfo.PtrOp ptr = (TypeInfo.PtrOp)iterator.next(); |
| |
| if( ptr.getType() == TypeInfo.PtrOp.t_reference ){ |
| if( ptr.isConst() || ptr.isVolatile() ){ |
| iterator.set( new PtrOp( PtrOp.t_undef, ptr.isConst(), ptr.isVolatile() ) ); |
| } else { |
| iterator.remove(); |
| } |
| cost.targetHadReference = true; |
| } |
| int size = targetPtrs.size(); |
| Iterator iter = targetPtrs.iterator(); |
| |
| for( int i = size; i > 0; i-- ){ |
| op = (TypeInfo.PtrOp) iter.next(); |
| if( op.getType() == TypeInfo.PtrOp.t_array ){ |
| op.setType( TypeInfo.PtrOp.t_pointer ); |
| } |
| } |
| } |
| |
| return cost; |
| } |
| |
| /** |
| * qualificationConversion |
| * @param cost |
| * |
| * see spec section 4.4 regarding qualification conversions |
| */ |
| static private void qualificationConversion( Cost cost ){ |
| int size = cost.source.getPtrOperators().size(); |
| int size2 = cost.target.getPtrOperators().size(); |
| |
| TypeInfo.PtrOp op1 = null, op2 = null; |
| boolean canConvert = true; |
| |
| Iterator iter1 = cost.source.getPtrOperators().iterator(); |
| Iterator iter2 = cost.target.getPtrOperators().iterator(); |
| |
| if( size != size2 ){ |
| if( size2 - size == 1 ){ |
| op2 = (PtrOp) iter2.next(); |
| if( op2.isConst() || op2.isVolatile() ){ |
| canConvert = true; |
| } else { |
| canConvert = false; |
| } |
| } else { |
| canConvert = false; |
| } |
| } else if ( size == 1 ){ |
| op1 = (TypeInfo.PtrOp) iter1.next(); |
| op2 = (TypeInfo.PtrOp) iter2.next(); |
| |
| //can only convert if op2 is more qualified |
| if( ( op1.isConst() && !op2.isConst() ) || |
| ( op1.isVolatile() && !op2.isVolatile() ) ) |
| { |
| cost.qualification = 0; |
| return; |
| } |
| canConvert = true; |
| } else if( size > 0 ){ |
| op1 = (TypeInfo.PtrOp) iter1.next(); |
| op2 = (TypeInfo.PtrOp) iter2.next(); |
| |
| boolean constInEveryCV2k = true; |
| |
| for( int j= 1; j < size; j++ ){ |
| op1 = (TypeInfo.PtrOp) iter1.next(); |
| op2 = (TypeInfo.PtrOp) iter2.next(); |
| |
| //pointer types must be similar |
| if( op1.getType() != op2.getType() ){ |
| canConvert = false; |
| break; |
| } |
| //if const is in cv1,j then const is in cv2,j. Similary for volatile |
| if( ( op1.isConst() && !op2.isConst() ) || |
| ( op1.isVolatile() && !op2.isVolatile() ) ) |
| { |
| canConvert = false; |
| break; |
| } |
| |
| //if cv1,j and cv2,j are different then const is in every cv2,k for 0<k<j |
| if( ( op1.compareCVTo( op2 ) != 0 ) && !constInEveryCV2k ){ |
| canConvert = false; |
| break; |
| } |
| |
| constInEveryCV2k &= op2.isConst(); |
| } |
| } |
| |
| if( canConvert == true ){ |
| cost.qualification = 1; |
| cost.rank = Cost.LVALUE_OR_QUALIFICATION_RANK; |
| } else { |
| cost.qualification = 0; |
| } |
| } |
| |
| /** |
| * |
| * @param source |
| * @param target |
| * @return int |
| * |
| * 4.5-1 char, signed char, unsigned char, short int or unsigned short int |
| * can be converted to int if int can represent all the values of the source |
| * type, otherwise they can be converted to unsigned int. |
| * 4.5-2 wchar_t or an enumeration can be converted to the first of the |
| * following that can hold it: int, unsigned int, long unsigned long. |
| * 4.5-4 bool can be promoted to int |
| * 4.6 float can be promoted to double |
| */ |
| static private void promotion( Cost cost ){ |
| TypeInfo src = cost.source; |
| TypeInfo trg = cost.target; |
| |
| int mask = TypeInfo.isShort | TypeInfo.isLong | TypeInfo.isUnsigned; |
| |
| if( (src.isType( TypeInfo.t_bool, TypeInfo.t_float ) || src.isType( TypeInfo.t_enumeration )) && |
| (trg.isType( TypeInfo.t_int ) || trg.isType( TypeInfo.t_double )) ) |
| { |
| if( src.getType() == trg.getType() && (( src.getTypeInfo() & mask) == (trg.getTypeInfo() & mask)) ){ |
| //same, no promotion needed |
| return; |
| } |
| |
| if( src.isType( TypeInfo.t_float ) ){ |
| cost.promotion = trg.isType( TypeInfo.t_double ) ? 1 : 0; |
| } else { |
| cost.promotion = ( trg.isType( TypeInfo.t_int ) && trg.canHold( src ) ) ? 1 : 0; |
| } |
| |
| } else { |
| cost.promotion = 0; |
| } |
| |
| cost.rank = (cost.promotion > 0 ) ? Cost.PROMOTION_RANK : Cost.NO_MATCH_RANK; |
| } |
| |
| /** |
| * |
| * @param source |
| * @param target |
| * @return int |
| * |
| */ |
| static private void conversion( Cost cost ){ |
| TypeInfo src = cost.source; |
| TypeInfo trg = cost.target; |
| |
| int temp = -1; |
| |
| cost.conversion = 0; |
| cost.detail = 0; |
| |
| if( !src.hasSamePtrs( trg ) ){ |
| return; |
| } |
| if( src.hasPtrOperators() && src.getPtrOperators().size() == 1 ){ |
| TypeInfo.PtrOp ptr = (TypeInfo.PtrOp)src.getPtrOperators().iterator().next(); |
| ISymbol srcDecl = src.isType( TypeInfo.t_type ) ? src.getTypeSymbol() : null; |
| ISymbol trgDecl = trg.isType( TypeInfo.t_type ) ? trg.getTypeSymbol() : null; |
| if( ptr.getType() == TypeInfo.PtrOp.t_pointer ){ |
| if( srcDecl == null || (trgDecl == null && !trg.isType( TypeInfo.t_void )) ){ |
| return; |
| } |
| |
| //4.10-2 an rvalue of type "pointer to cv T", where T is an object type can be |
| //converted to an rvalue of type "pointer to cv void" |
| if( trg.isType( TypeInfo.t_void ) ){ |
| cost.rank = Cost.CONVERSION_RANK; |
| cost.conversion = 1; |
| cost.detail = 2; |
| return; |
| } |
| |
| cost.detail = 1; |
| |
| //4.10-3 An rvalue of type "pointer to cv D", where D is a class type can be converted |
| // to an rvalue of type "pointer to cv B", where B is a base class of D. |
| if( (srcDecl instanceof IDerivableContainerSymbol) && trgDecl.isType( srcDecl.getType() ) ){ |
| try { |
| temp = hasBaseClass( (IDerivableContainerSymbol) srcDecl, (IDerivableContainerSymbol) trgDecl ); |
| } catch (ParserSymbolTableException e) { |
| //not going to happen since we didn't ask for the visibility exception |
| } |
| cost.rank = ( temp > -1 ) ? Cost.CONVERSION_RANK : Cost.NO_MATCH_RANK; |
| cost.conversion = ( temp > -1 ) ? temp : 0; |
| cost.detail = 1; |
| return; |
| } |
| } else if( ptr.getType() == TypeInfo.PtrOp.t_memberPointer ){ |
| //4.11-2 An rvalue of type "pointer to member of B of type cv T", where B is a class type, |
| //can be converted to an rvalue of type "pointer to member of D of type cv T" where D is a |
| //derived class of B |
| if( srcDecl == null || trgDecl == null ){ |
| return; |
| } |
| |
| TypeInfo.PtrOp srcPtr = trg.hasPtrOperators() ? (TypeInfo.PtrOp)trg.getPtrOperators().iterator().next() : null; |
| if( trgDecl.isType( srcDecl.getType() ) && srcPtr != null && srcPtr.getType() == TypeInfo.PtrOp.t_memberPointer ){ |
| try { |
| temp = hasBaseClass( (IDerivableContainerSymbol)ptr.getMemberOf(), (IDerivableContainerSymbol)srcPtr.getMemberOf() ); |
| } catch (ParserSymbolTableException e) { |
| //not going to happen since we didn't ask for the visibility exception |
| } |
| cost.rank = ( temp > -1 ) ? Cost.CONVERSION_RANK : Cost.NO_MATCH_RANK; |
| cost.detail = 1; |
| cost.conversion = ( temp > -1 ) ? temp : 0; |
| return; |
| } |
| } |
| } else if( !src.hasPtrOperators() ) { |
| //4.7 An rvalue of an integer type can be converted to an rvalue of another integer type. |
| //An rvalue of an enumeration type can be converted to an rvalue of an integer type. |
| if( src.isType( TypeInfo.t_bool, TypeInfo.t_int ) || |
| src.isType( TypeInfo.t_float, TypeInfo.t_double ) || |
| src.isType( TypeInfo.t_enumeration ) ) |
| { |
| if( trg.isType( TypeInfo.t_bool, TypeInfo.t_int ) || |
| trg.isType( TypeInfo.t_float, TypeInfo.t_double ) ) |
| { |
| cost.rank = Cost.CONVERSION_RANK; |
| cost.conversion = 1; |
| } |
| } |
| } |
| } |
| |
| static private void derivedToBaseConversion( Cost cost ) throws ParserSymbolTableException{ |
| TypeInfo src = cost.source; |
| TypeInfo trg = cost.target; |
| |
| ISymbol srcDecl = src.isType( TypeInfo.t_type ) ? src.getTypeSymbol() : null; |
| ISymbol trgDecl = trg.isType( TypeInfo.t_type ) ? trg.getTypeSymbol() : null; |
| |
| if( !src.hasSamePtrs( trg ) || srcDecl == null || trgDecl == null || !cost.targetHadReference ){ |
| return; |
| } |
| |
| int temp = hasBaseClass( (IDerivableContainerSymbol) srcDecl, (IDerivableContainerSymbol) trgDecl, true ); |
| |
| if( temp > -1 ){ |
| cost.rank = Cost.DERIVED_TO_BASE_CONVERSION; |
| cost.conversion = temp; |
| } |
| } |
| |
| static private Cost checkStandardConversionSequence( TypeInfo source, TypeInfo target ) throws ParserSymbolTableException{ |
| Cost cost = lvalue_to_rvalue( source, target ); |
| |
| if( cost.source == null || cost.target == null ){ |
| return cost; |
| } |
| |
| if( cost.source.equals( cost.target ) ){ |
| cost.rank = Cost.IDENTITY_RANK; |
| return cost; |
| } |
| |
| qualificationConversion( cost ); |
| |
| //if we can't convert the qualifications, then we can't do anything |
| if( cost.qualification == 0 ){ |
| return cost; |
| } |
| |
| //was the qualification conversion enough? |
| if( cost.source.isType( TypeInfo.t_type ) && cost.target.isType( TypeInfo.t_type ) ){ |
| if( cost.target.hasSamePtrs( cost.source ) ){ |
| ISymbol srcSymbol = cost.source.getTypeSymbol(); |
| ISymbol trgSymbol = cost.target.getTypeSymbol(); |
| if( srcSymbol != null && trgSymbol != null ){ |
| if( srcSymbol.equals( trgSymbol ) ) |
| { |
| return cost; |
| } |
| } |
| } |
| } |
| |
| promotion( cost ); |
| if( cost.promotion > 0 || cost.rank > -1 ){ |
| return cost; |
| } |
| |
| conversion( cost ); |
| |
| if( cost.rank > -1 ) |
| return cost; |
| |
| derivedToBaseConversion( cost ); |
| |
| return cost; |
| } |
| |
| static private Cost checkUserDefinedConversionSequence( TypeInfo source, TypeInfo target ) throws ParserSymbolTableException { |
| Cost cost = null; |
| Cost constructorCost = null; |
| Cost conversionCost = null; |
| |
| ISymbol targetDecl = null; |
| ISymbol sourceDecl = null; |
| IParameterizedSymbol constructor = null; |
| IParameterizedSymbol conversion = null; |
| |
| //constructors |
| if( target.getType() == TypeInfo.t_type ){ |
| targetDecl = target.getTypeSymbol(); |
| if( targetDecl.isType( TypeInfo.t_class, TypeInfo.t_union ) ){ |
| LookupData data = new LookupData( EMPTY_NAME, TypeInfo.t_constructor, null ); |
| data.parameters = new LinkedList(); |
| data.parameters.add( source ); |
| data.forUserDefinedConversion = true; |
| |
| IDerivableContainerSymbol container = (IDerivableContainerSymbol) targetDecl; |
| |
| if( targetDecl instanceof TemplateInstance ){ |
| data.templateInstance = targetDecl; |
| container = (IDerivableContainerSymbol)((TemplateInstance) targetDecl).getInstantiatedSymbol(); |
| } |
| |
| if( !container.getConstructors().isEmpty() ){ |
| LinkedList constructors = new LinkedList( container.getConstructors() ); |
| constructor = resolveFunction( data, constructors ); |
| } |
| if( constructor != null && constructor.getTypeInfo().checkBit( TypeInfo.isExplicit ) ){ |
| constructor = null; |
| } |
| |
| } |
| } |
| |
| //conversion operators |
| if( source.getType() == TypeInfo.t_type ){ |
| source = getFlatTypeInfo( source ); |
| sourceDecl = ( source != null ) ? source.getTypeSymbol() : null; |
| |
| if( sourceDecl != null && (sourceDecl instanceof IContainerSymbol) ){ |
| String name = target.toString(); |
| |
| if( !name.equals(EMPTY_NAME) ){ |
| LookupData data = new LookupData( "operator " + name, TypeInfo.t_function, null ); //$NON-NLS-1$ |
| LinkedList params = new LinkedList(); |
| data.parameters = params; |
| data.forUserDefinedConversion = true; |
| |
| data.foundItems = lookupInContained( data, (IContainerSymbol) sourceDecl ); |
| conversion = (IParameterizedSymbol)resolveAmbiguities( data ); |
| } |
| } |
| } |
| |
| if( constructor != null ){ |
| constructorCost = checkStandardConversionSequence( (TypeInfo) new TypeInfo( TypeInfo.t_type, 0, constructor.getContainingSymbol() ), target ); |
| } |
| if( conversion != null ){ |
| conversionCost = checkStandardConversionSequence( (TypeInfo) new TypeInfo( target.getType(), 0, target.getTypeSymbol() ), target ); |
| } |
| |
| //if both are valid, then the conversion is ambiguous |
| if( constructorCost != null && constructorCost.rank != Cost.NO_MATCH_RANK && |
| conversionCost != null && conversionCost.rank != Cost.NO_MATCH_RANK ) |
| { |
| cost = constructorCost; |
| cost.userDefined = Cost.AMBIGUOUS_USERDEFINED_CONVERSION; |
| cost.rank = Cost.USERDEFINED_CONVERSION_RANK; |
| } else { |
| if( constructorCost != null && constructorCost.rank != Cost.NO_MATCH_RANK ){ |
| cost = constructorCost; |
| cost.userDefined = constructor.hashCode(); |
| cost.rank = Cost.USERDEFINED_CONVERSION_RANK; |
| } else if( conversionCost != null && conversionCost.rank != Cost.NO_MATCH_RANK ){ |
| cost = conversionCost; |
| cost.userDefined = conversion.hashCode(); |
| cost.rank = Cost.USERDEFINED_CONVERSION_RANK; |
| } |
| } |
| |
| return cost; |
| } |
| |
| /** |
| * Determine the type of a conditional operator based on the second and third operands |
| * @param secondOp |
| * @param thirdOp |
| * @return |
| * Spec 5.16 |
| * Determine if the second operand can be converted to match the third operand, and vice versa. |
| * - If both can be converted, or one can be converted but the conversion is ambiguous, the program |
| * is illformed (throw ParserSymbolTableException) |
| * - If neither can be converted, further checking must be done (return null) |
| * - If exactly one conversion is possible, that conversion is applied ( return the other TypeInfo ) |
| */ |
| static public TypeInfo getConditionalOperand( TypeInfo secondOp, TypeInfo thirdOp ) throws ParserSymbolTableException{ |
| |
| //can secondOp convert to thirdOp ? |
| Cost secondCost = checkStandardConversionSequence( secondOp, getFlatTypeInfo( thirdOp ) ); |
| |
| if( secondCost.rank == Cost.NO_MATCH_RANK ){ |
| secondCost = checkUserDefinedConversionSequence( secondOp, getFlatTypeInfo( thirdOp ) ); |
| } |
| |
| Cost thirdCost = checkStandardConversionSequence( thirdOp, getFlatTypeInfo( secondOp ) ); |
| if( thirdCost.rank == Cost.NO_MATCH_RANK ){ |
| thirdCost = checkUserDefinedConversionSequence( thirdOp, getFlatTypeInfo( secondOp ) ); |
| } |
| |
| |
| boolean canConvertSecond = ( secondCost != null && secondCost.rank != Cost.NO_MATCH_RANK ); |
| boolean canConvertThird = ( thirdCost != null && thirdCost.rank != Cost.NO_MATCH_RANK ); |
| |
| if( !canConvertSecond && !canConvertThird ){ |
| //neither can be converted |
| return null; |
| } else if ( canConvertSecond && canConvertThird ){ |
| //both can be converted -> illformed |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_Ambiguous ); |
| } else { |
| if( canConvertSecond ){ |
| if( secondCost.userDefined == Cost.AMBIGUOUS_USERDEFINED_CONVERSION ){ |
| //conversion is ambiguous -> ill-formed |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_Ambiguous ); |
| } else { |
| return thirdOp; |
| } |
| } else { |
| if( thirdCost.userDefined == Cost.AMBIGUOUS_USERDEFINED_CONVERSION ){ |
| //conversion is ambiguous -> ill-formed |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_Ambiguous ); |
| } else { |
| return secondOp; |
| } |
| } |
| } |
| } |
| |
| /** |
| * |
| * @param decl |
| * @return TypeInfo |
| * The top level TypeInfo represents modifications to the object and the |
| * remaining TypeInfo's represent the object. |
| */ |
| // TODO move this to ITypeInfo ? |
| static protected TypeInfo getFlatTypeInfo( TypeInfo topInfo ){ |
| TypeInfo returnInfo = topInfo; |
| TypeInfo info = null; |
| |
| if( topInfo.getType() == TypeInfo.t_type && topInfo.getTypeSymbol() != null ){ |
| returnInfo = (TypeInfo)new TypeInfo(); |
| |
| ISymbol typeSymbol = topInfo.getTypeSymbol(); |
| |
| info = typeSymbol.getTypeInfo(); |
| |
| while( info.getTypeSymbol() != null && ( info.getType() == TypeInfo.t_type || info.isForwardDeclaration() ) ){ |
| typeSymbol = info.getTypeSymbol(); |
| |
| returnInfo.addPtrOperator( info.getPtrOperators() ); |
| |
| info = typeSymbol.getTypeInfo(); |
| } |
| |
| if( info.isType( TypeInfo.t_class, TypeInfo.t_enumeration ) ){ |
| returnInfo.setType( TypeInfo.t_type ); |
| returnInfo.setTypeSymbol( typeSymbol ); |
| } else { |
| returnInfo.setTypeInfo( info.getTypeInfo() ); |
| returnInfo.setType( info.getType() ); |
| returnInfo.setTypeSymbol( null ); |
| returnInfo.addPtrOperator( info.getPtrOperators() ); |
| } |
| |
| returnInfo.applyOperatorExpressions( topInfo.getOperatorExpressions() ); |
| |
| if( topInfo.hasPtrOperators() ){ |
| TypeInfo.PtrOp topPtr = (PtrOp) topInfo.getPtrOperators().iterator().next(); |
| TypeInfo.PtrOp ptr = null; |
| if( returnInfo.hasPtrOperators() ){ |
| ptr = (PtrOp)returnInfo.getPtrOperators().iterator().next(); |
| } else { |
| ptr = new PtrOp(); |
| returnInfo.addPtrOperator( ptr ); |
| ptr.setType( topPtr.getType() ); |
| } |
| |
| ptr.setConst( topPtr.isConst() ); |
| ptr.setVolatile( topPtr.isVolatile() ); |
| } |
| } else { |
| returnInfo = new TypeInfo( topInfo ); |
| } |
| |
| return returnInfo; |
| } |
| |
| static private IParameterizedSymbol matchTemplatePartialSpecialization( IParameterizedSymbol template, List args ){ |
| if( template == null ){ |
| return null; |
| } |
| |
| List specs = template.getSpecializations(); |
| int size = ( specs != null ) ? specs.size() : 0; |
| if( size == 0 ){ |
| return template; |
| } |
| |
| IParameterizedSymbol bestMatch = null; |
| boolean bestMatchIsBest = true; |
| Iterator iter = specs.iterator(); |
| IParameterizedSymbol spec = null; |
| List specArgs = null; |
| for( int i = size; i > 0; i-- ){ |
| spec = (IParameterizedSymbol) iter.next(); |
| specArgs = spec.getArgumentList(); |
| if( specArgs == null || specArgs.size() != args.size() ){ |
| continue; |
| } |
| |
| ISymbol sym1 = null, sym2 = null; |
| Iterator iter1 = specArgs.iterator(); |
| Iterator iter2 = args.iterator(); |
| |
| HashMap map = new HashMap(); |
| //String name = null; |
| boolean match = true; |
| for( int j = specArgs.size(); j > 0; j-- ){ |
| sym1 = (ISymbol)iter1.next(); |
| TypeInfo info2 = (TypeInfo) iter2.next(); |
| if( info2.isType( TypeInfo.t_type ) ){ |
| sym2 = sym2.getTypeSymbol(); |
| } else { |
| sym2 = template.getSymbolTable().newSymbol( EMPTY_NAME ); |
| sym2.setTypeInfo( info2 ); |
| } |
| |
| if( !deduceTemplateArgument( map, sym1, sym2, null ) ){ |
| match = false; |
| break; |
| } |
| /* |
| name = sym1.getName(); |
| if( name.equals( "" ) ){ |
| //no name, only type |
| } else if( map.containsKey( name ) ) { |
| ISymbol val = (ISymbol) map.get( name ); |
| if( val.getType() != sym2.getType() ){ |
| match = false; |
| break; |
| } |
| } else { |
| map.put( name, sym2 ); |
| } |
| */ |
| } |
| if( match ){ |
| int compare = orderSpecializations( bestMatch, spec ); |
| if( compare == 0 ){ |
| bestMatchIsBest = false; |
| } else if( compare < 0 ) { |
| bestMatch = spec; |
| bestMatchIsBest = true; |
| } |
| } |
| } |
| |
| return bestMatchIsBest ? bestMatch : null; |
| } |
| |
| /** |
| * Compare spec1 to spec2. Return > 0 if spec1 is more specialized, < 0 if spec2 |
| * is more specialized, = 0 otherwise. |
| * @param spec1 |
| * @param spec2 |
| * @return |
| */ |
| static private int orderSpecializations( IParameterizedSymbol spec1, IParameterizedSymbol spec2 ){ |
| if( spec1 == null ){ |
| return -1; |
| } |
| |
| Iterator iter = spec1.getContainedSymbols().keySet().iterator(); |
| ISymbol decl = (ISymbol) spec1.getContainedSymbols().get( iter.next() ); |
| |
| //to order class template specializations, we need to transform them into function templates |
| if( decl.isType( TypeInfo.t_class ) ) { |
| spec1 = classTemplateSpecializationToFunctionTemplate( spec1 ); |
| spec2 = classTemplateSpecializationToFunctionTemplate( spec2 ); |
| } |
| |
| TemplateInstance transformed1 = transformFunctionTemplateForOrdering( spec1 ); |
| TemplateInstance transformed2 = transformFunctionTemplateForOrdering( spec2 ); |
| |
| //Using the transformed parameter list, perform argument deduction against the other |
| //function template |
| boolean d1 = deduceTemplateArguments( spec2, transformed1 ); |
| boolean d2 = deduceTemplateArguments( spec1, transformed2 ); |
| |
| //The transformed template is at least as specialized as the other iff the deduction |
| //succeeds and the deduced parameter types are an exact match |
| //A template is more specialized than another iff it is at least as specialized as the |
| //other template and that template is not at least as specialized as the first. |
| if( d1 && d2 || !d1 && !d2 ) |
| return 0; |
| else if( d1 && !d2 ) |
| return 1; |
| else |
| return -1; |
| } |
| |
| /** |
| * |
| * @param template |
| * @param args |
| * @return |
| * |
| * A type that is specified in terms of template parameters (P) is compared with an actual |
| * type (A), and an attempt is made to find template argument vaules that will make P, |
| * after substitution of the deduced values, compatible with A. |
| */ |
| static private boolean deduceTemplateArguments( IParameterizedSymbol template, TemplateInstance argSource ){ |
| if( template.getContainedSymbols() == null || template.getContainedSymbols().size() != 1 ){ |
| return false; |
| } |
| Iterator iter = template.getContainedSymbols().keySet().iterator(); |
| ISymbol templateSymbol = (ISymbol) template.getContainedSymbols().get( iter.next() ); |
| if( !templateSymbol.isType( TypeInfo.t_function ) ){ |
| return false; |
| } |
| |
| IParameterizedSymbol argTemplate = (IParameterizedSymbol)argSource.getInstantiatedSymbol(); |
| iter = argTemplate.getContainedSymbols().keySet().iterator(); |
| ISymbol argFunction = (ISymbol) argTemplate.getContainedSymbols().get( iter.next() ); |
| if( !argFunction.isType( TypeInfo.t_function ) ){ |
| return false; |
| } |
| |
| List args = ((IParameterizedSymbol) argFunction).getParameterList(); |
| |
| IParameterizedSymbol function = (IParameterizedSymbol) templateSymbol; |
| |
| if( function.getParameterList() == null || function.getParameterList().size() != args.size() ){ |
| return false; |
| } |
| |
| HashMap map = new HashMap(); |
| |
| Iterator pIter = function.getParameterList().iterator(); |
| Iterator aIter = args.iterator(); |
| while( pIter.hasNext() ){ |
| if( !deduceTemplateArgument( map, (ISymbol) pIter.next(), (ISymbol) aIter.next(), argSource.getArgumentMap() ) ){ |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| static private boolean deduceTemplateArgument( Map map, ISymbol p, ISymbol a, Map argumentMap ){ |
| if( argumentMap != null && argumentMap.containsKey( a ) ){ |
| a = (ISymbol) argumentMap.get( a ); |
| } |
| |
| ISymbol pSymbol = p, aSymbol = a; |
| |
| if( p.isType( TypeInfo.t_type ) ){ |
| pSymbol = p.getTypeSymbol(); |
| aSymbol = a.isType( TypeInfo.t_type ) ? a.getTypeSymbol() : a; |
| return deduceTemplateArgument( map, pSymbol, aSymbol, argumentMap ); |
| } else { |
| if( pSymbol.isTemplateMember() && pSymbol.isType( TypeInfo.t_undef ) ){ |
| //T* or T& or T[ const ] |
| //also |
| List pPtrs = pSymbol.getPtrOperators(); |
| List aPtrs = aSymbol.getPtrOperators(); |
| |
| if( pPtrs != null ){ |
| TypeInfo.PtrOp pOp = (TypeInfo.PtrOp) pPtrs.iterator().next();; |
| TypeInfo.PtrOp aOp = ( aPtrs != null ) ? (TypeInfo.PtrOp)pPtrs.iterator().next() : null; |
| |
| if( pOp != null && aOp != null && pOp.getType() == aOp.getType() ){ |
| if( pOp.getType() == TypeInfo.PtrOp.t_memberPointer ){ |
| |
| } else { |
| TypeInfo type = new TypeInfo( aSymbol.getTypeInfo() ); |
| type.getPtrOperators().clear(); |
| map.put( pSymbol.getName(), type ); |
| return true; |
| } |
| } else { |
| return false; |
| } |
| } else { |
| //T |
| map.put( pSymbol.getName(), a.getTypeInfo() ); |
| return true; |
| } |
| |
| |
| } |
| //template-name<T> or template-name<i> |
| else if( pSymbol.isType( TypeInfo.t_template ) && aSymbol.isType( TypeInfo.t_template ) ){ |
| List pArgs = ((IParameterizedSymbol)pSymbol).getArgumentList(); |
| List aArgs = ((IParameterizedSymbol)aSymbol).getArgumentList(); |
| |
| if( pArgs == null || aArgs == null || pArgs.size() != aArgs.size()){ |
| return false; |
| } |
| Iterator pIter = pArgs.iterator(); |
| Iterator aIter = aArgs.iterator(); |
| while( pIter.hasNext() ){ |
| if( !deduceTemplateArgument( map, (ISymbol) pIter.next(), (ISymbol) aIter.next(), argumentMap ) ){ |
| return false; |
| } |
| } |
| } |
| //T (*) ( ), T ( T::* ) ( T ), & variations |
| else if( pSymbol.isType( TypeInfo.t_function ) && aSymbol.isType( TypeInfo.t_function ) ){ |
| IParameterizedSymbol pFunction = (IParameterizedSymbol)pSymbol; |
| IParameterizedSymbol aFunction = (IParameterizedSymbol)aSymbol; |
| |
| if( !deduceTemplateArgument( map, aFunction.getReturnType(), pFunction.getReturnType(), argumentMap ) ){ |
| return false; |
| } |
| if( pSymbol.getPtrOperators() != null ){ |
| List ptrs = pSymbol.getPtrOperators(); |
| TypeInfo.PtrOp op = (TypeInfo.PtrOp) ptrs.iterator().next();; |
| if( op.getType() == TypeInfo.PtrOp.t_memberPointer ){ |
| if( !deduceTemplateArgument( map, op.getMemberOf(), pFunction.getContainingSymbol(), argumentMap ) ){ |
| return false; |
| } |
| } |
| } |
| |
| List pParams = pFunction.getParameterList(); |
| List aParams = aFunction.getParameterList(); |
| if( pParams.size() != aParams.size() ){ |
| return false; |
| } else { |
| Iterator pIter = pParams.iterator(); |
| Iterator aIter = aParams.iterator(); |
| while( pIter.hasNext() ){ |
| if( !deduceTemplateArgument( map, (ISymbol) pIter.next(), (ISymbol) aIter.next(), argumentMap ) ){ |
| return false; |
| } |
| } |
| } |
| |
| } else if( pSymbol.getType() == aSymbol.getType() ){ |
| if( pSymbol.getTypeInfo().getHasDefault() ){ |
| if( !aSymbol.getTypeInfo().getHasDefault() || |
| aSymbol.getTypeInfo().getDefault().equals( pSymbol.getTypeInfo().getDefault() ) ) |
| { |
| return false; |
| } |
| } |
| //value |
| map.put( pSymbol.getName(), aSymbol.getTypeInfo() ); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| /** |
| * transform the class template to a function template as described in the spec |
| * 14.5.4.2-1 |
| * @param template |
| * @return IParameterizedSymbol |
| * the function template has the same template parameters as the partial specialization and |
| * has a single function parameter whose type is a class template specialization with the template |
| * arguments of the partial specialization |
| */ |
| static private IParameterizedSymbol classTemplateSpecializationToFunctionTemplate( IParameterizedSymbol template ){ |
| IParameterizedSymbol transformed = (IParameterizedSymbol) template.clone(); |
| transformed.getArgumentList().clear(); |
| transformed.getContainedSymbols().clear(); |
| |
| IParameterizedSymbol function = template.getSymbolTable().newParameterizedSymbol( transformed.getName(), TypeInfo.t_function ); |
| try{ |
| transformed.addSymbol( function ); |
| } catch ( ParserSymbolTableException e ){ |
| //we shouldn't get this because there aren't any other symbols in the template |
| } |
| |
| function.addParameter( template ); |
| |
| return transformed; |
| } |
| |
| /** |
| * transform a function template for use in partial ordering, as described in the |
| * spec 14.5.5.2-3 |
| * @param template |
| * @return |
| * -for each type template parameter, synthesize a unique type and substitute that for each |
| * occurence of that parameter in the function parameter list |
| * -for each non-type template parameter, synthesize a unique value of the appropriate type and |
| * susbstitute that for each occurence of that parameter in the function parameter list |
| * for each template template parameter, synthesize a unique class template and substitute that |
| * for each occurence of that parameter in the function parameter list |
| */ |
| static private TemplateInstance transformFunctionTemplateForOrdering( IParameterizedSymbol template ){ |
| |
| List paramList = template.getParameterList(); |
| |
| int size = ( paramList != null ) ? paramList.size() : 0; |
| if( size == 0 ){ |
| return null; |
| } |
| |
| HashMap map = new HashMap(); |
| for( Iterator iterator = paramList.iterator(); iterator.hasNext(); ) { |
| ISymbol param = (ISymbol) iterator.next(); |
| ISymbol val = template.getSymbolTable().newSymbol( EMPTY_NAME, TypeInfo.t_type ); |
| if( false /* is value */ ){ |
| //val.getTypeInfo().setHasDefault() |
| } |
| map.put( param, val ); |
| } |
| |
| return new TemplateInstance( template.getSymbolTable(), template, map ); |
| } |
| |
| //private Stack _contextStack = new Stack(); |
| private IContainerSymbol _compilationUnit; |
| private ParserLanguage _language; |
| private LinkedList undoList = new LinkedList(); |
| private HashSet markSet = new HashSet(); |
| |
| public void setLanguage( ParserLanguage language ){ |
| _language = language; |
| } |
| |
| public ParserLanguage getLanguage(){ |
| return _language; |
| } |
| |
| protected void pushCommand( Command command ){ |
| undoList.addFirst( command ); |
| } |
| |
| public Mark setMark(){ |
| Mark mark = new Mark(); |
| undoList.addFirst( mark ); |
| markSet.add( mark ); |
| return mark; |
| } |
| |
| public boolean rollBack( Mark toMark ){ |
| if( markSet.contains( toMark ) ){ |
| markSet.remove( toMark ); |
| Command command = ( Command )undoList.removeFirst(); |
| while( command != toMark ){ |
| command.undoIt(); |
| command = ( Command ) undoList.removeFirst(); |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| public boolean commit( Mark toMark ){ |
| if( markSet.contains( toMark ) ){ |
| markSet.remove( toMark ); |
| Command command = ( Command )undoList.removeLast(); |
| while( command != toMark ){ |
| command = (Command) undoList.removeLast(); |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static abstract protected class Command{ |
| abstract public void undoIt(); |
| } |
| |
| static public class Mark extends Command{ |
| public void undoIt(){ }; |
| } |
| |
| |
| static public class LookupMode extends Enum{ |
| public static final LookupMode PREFIX = new LookupMode( 1 ); |
| public static final LookupMode NORMAL = new LookupMode( 2 ); |
| |
| private LookupMode( int constant) |
| { |
| super( constant ); |
| } |
| } |
| |
| |
| static protected class LookupData |
| { |
| public Set ambiguities; |
| public String name; |
| public Map usingDirectives; |
| public Set visited = new HashSet(); //used to ensure we don't visit things more than once |
| |
| public HashSet inheritanceChain; //used to detect circular inheritance |
| |
| public List parameters; //parameter info for resolving functions |
| public HashSet associated; //associated namespaces for argument dependant lookup |
| public ISymbol stopAt; //stop looking along the stack once we hit this declaration |
| public TypeFilter filter = null; |
| |
| public boolean qualified = false; |
| public boolean ignoreUsingDirectives = false; |
| public boolean usingDirectivesOnly = false; |
| public boolean forUserDefinedConversion = false; |
| |
| public Map foundItems = null; |
| |
| public ISymbol templateInstance = null; |
| public LookupMode mode = LookupMode.NORMAL; |
| |
| public LookupData( String n, TypeInfo.eType t, ISymbol i ){ |
| name = n; |
| filter = new TypeFilter( t ); |
| templateInstance = i; |
| } |
| public LookupData( String n, TypeFilter f, ISymbol i ){ |
| name = n; |
| filter = ( f != null ) ? f : new TypeFilter( TypeInfo.t_any ); |
| templateInstance = i; |
| } |
| } |
| |
| static private class Cost |
| { |
| |
| public Cost( TypeInfo s, TypeInfo t ){ |
| source = ( s != null ) ? new TypeInfo( s ) : new TypeInfo(); |
| target = ( t != null ) ? new TypeInfo( t ) : new TypeInfo(); |
| } |
| |
| public TypeInfo source; |
| public TypeInfo target; |
| |
| public boolean targetHadReference = false; |
| |
| public int lvalue; |
| public int promotion; |
| public int conversion; |
| public int qualification; |
| public int userDefined; |
| public int rank = -1; |
| public int detail; |
| |
| //Some constants to help clarify things |
| public static final int AMBIGUOUS_USERDEFINED_CONVERSION = 1; |
| |
| public static final int NO_MATCH_RANK = -1; |
| public static final int IDENTITY_RANK = 0; |
| public static final int LVALUE_OR_QUALIFICATION_RANK = 0; |
| public static final int PROMOTION_RANK = 1; |
| public static final int CONVERSION_RANK = 2; |
| public static final int DERIVED_TO_BASE_CONVERSION = 3; |
| public static final int USERDEFINED_CONVERSION_RANK = 4; |
| public static final int ELLIPSIS_CONVERSION = 5; |
| |
| |
| public int compare( Cost cost ){ |
| int result = 0; |
| |
| if( rank != cost.rank ){ |
| return cost.rank - rank; |
| } |
| |
| if( userDefined != 0 || cost.userDefined != 0 ){ |
| if( userDefined == 0 || cost.userDefined == 0 ){ |
| return cost.userDefined - userDefined; |
| } else { |
| if( (userDefined == AMBIGUOUS_USERDEFINED_CONVERSION || cost.userDefined == AMBIGUOUS_USERDEFINED_CONVERSION) || |
| (userDefined != cost.userDefined ) ) |
| { |
| return 0; |
| } |
| // else they are the same constructor/conversion operator and are ranked |
| //on the standard conversion sequence |
| } |
| } |
| |
| if( promotion > 0 || cost.promotion > 0 ){ |
| result = cost.promotion - promotion; |
| } |
| if( conversion > 0 || cost.conversion > 0 ){ |
| if( detail == cost.detail ){ |
| result = cost.conversion - conversion; |
| } else { |
| result = cost.detail - detail; |
| } |
| } |
| |
| if( result == 0 ){ |
| if( cost.qualification != qualification ){ |
| return cost.qualification - qualification; |
| } else if( (cost.qualification == qualification) && qualification == 0 ){ |
| return 0; |
| } else { |
| int size = cost.target.hasPtrOperators() ? cost.target.getPtrOperators().size() : 0; |
| int size2 = target.hasPtrOperators() ? target.getPtrOperators().size() : 0; |
| |
| ListIterator iter1 = cost.target.getPtrOperators().listIterator( size ); |
| ListIterator iter2 = target.getPtrOperators().listIterator( size2 ); |
| |
| TypeInfo.PtrOp op1 = null, op2 = null; |
| |
| int subOrSuper = 0; |
| for( int i = ( size < size2 ) ? size : size2; i > 0; i-- ){ |
| op1 = (TypeInfo.PtrOp)iter1.previous(); |
| op2 = (TypeInfo.PtrOp)iter2.previous(); |
| |
| if( subOrSuper == 0) |
| subOrSuper = op1.compareCVTo( op2 ); |
| else if( ( subOrSuper > 0 && ( op1.compareCVTo( op2 ) < 0 )) || |
| ( subOrSuper < 0 && ( op1.compareCVTo( op2 ) > 0 )) ) |
| { |
| result = -1; |
| break; |
| } |
| } |
| if( result == -1 ){ |
| result = 0; |
| } else { |
| if( size == size2 ){ |
| result = subOrSuper; |
| } else { |
| result = size - size2; |
| } |
| } |
| } |
| } |
| |
| return result; |
| } |
| } |
| |
| /** |
| * The visibility of the symbol is modified by the visibility of the base classes |
| * @param symbol |
| * @param qualifyingSymbol |
| * @return |
| */ |
| public static ASTAccessVisibility getVisibility(ISymbol symbol, IContainerSymbol qualifyingSymbol) throws ParserSymbolTableException { |
| |
| IContainerSymbol container = symbol.getContainingSymbol(); |
| if( qualifyingSymbol == null || container.equals( qualifyingSymbol ) ){ |
| ISymbolASTExtension extension = symbol.getASTExtension(); |
| IASTNode node = extension != null ? extension.getPrimaryDeclaration() : null; |
| if( node != null && node instanceof IASTMember ){ |
| return ((IASTMember)node).getVisiblity(); |
| } else { |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_InternalError ); |
| } |
| } |
| |
| if( ! (qualifyingSymbol instanceof IDerivableContainerSymbol) ){ |
| throw new ParserSymbolTableException( ParserSymbolTableException.r_InternalError ); |
| } |
| |
| List parents = ((IDerivableContainerSymbol) qualifyingSymbol).getParents(); |
| Iterator iter = parents.iterator(); |
| IParentSymbol parent = null; |
| ASTAccessVisibility symbolAccess = null; |
| ASTAccessVisibility parentAccess = null; |
| |
| while( iter.hasNext() ){ |
| parent = (IParentSymbol) iter.next(); |
| |
| if( container == parent.getParent() ){ |
| parentAccess = parent.getAccess(); |
| symbolAccess = ((IASTMember)symbol.getASTExtension().getPrimaryDeclaration()).getVisiblity(); |
| |
| return ( parentAccess.getEnumValue() > symbolAccess.getEnumValue() ) ? parentAccess : symbolAccess; |
| } |
| } |
| |
| iter = parents.iterator(); |
| |
| //if static or an enumerator, the symbol could be visible through more than one path through the heirarchy, |
| //so we need to check all paths |
| boolean checkAllPaths = ( symbol.isType( TypeInfo.t_enumerator ) || symbol.getTypeInfo().checkBit( TypeInfo.isStatic ) ); |
| ASTAccessVisibility resultingAccess = null; |
| while( iter.hasNext() ){ |
| parent = (IParentSymbol) iter.next(); |
| parentAccess = parent.getAccess(); |
| symbolAccess = getVisibility( symbol, (IContainerSymbol) parent.getParent() ); |
| |
| if( symbolAccess != null ){ |
| symbolAccess = ( parentAccess.getEnumValue() > symbolAccess.getEnumValue() ) ? parentAccess : symbolAccess; |
| if( checkAllPaths ){ |
| if( resultingAccess != null ) |
| resultingAccess = ( resultingAccess.getEnumValue() > symbolAccess.getEnumValue() ) ? symbolAccess : resultingAccess; |
| else |
| resultingAccess = symbolAccess; |
| } else { |
| return symbolAccess; |
| } |
| } |
| } |
| return resultingAccess; |
| } |
| } |