/******************************************************************************* * Copyright (c) 2004, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM - Initial API and implementation * Markus Schorn (Wind River Systems) * Bryan Wilkinson (QNX) * Andrew Ferguson (Symbian) * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics; import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.LVALUE; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.valueCategoryFromReturnType; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.ALLCVQ; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.COND_TDEF; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.CVTYPE; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.REF; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.addQualifiers; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.calculateInheritanceDepth; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getCVQualifier; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.isVoidType; import java.util.Collections; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IArrayType; import org.eclipse.cdt.core.dom.ast.IBasicType; import org.eclipse.cdt.core.dom.ast.IBasicType.Kind; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IEnumeration; import org.eclipse.cdt.core.dom.ast.IFunctionType; import org.eclipse.cdt.core.dom.ast.IPointerType; import org.eclipse.cdt.core.dom.ast.IProblemBinding; import org.eclipse.cdt.core.dom.ast.IQualifierType; import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.IValue; import org.eclipse.cdt.core.dom.ast.cpp.ICPPBasicType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPField; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace; import org.eclipse.cdt.core.dom.ast.cpp.ICPPPointerToMemberType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance; import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.ArithmeticConversion; import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerToMemberType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPQualifierType; import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost.DeferredUDC; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost.Rank; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost.ReferenceBinding; /** * Routines for calculating the cost of conversions. */ public class Conversions { public enum UDCMode { ALLOWED, FORBIDDEN, DEFER } public enum Context { ORDINARY, IMPLICIT_OBJECT_FOR_METHOD_WITHOUT_REF_QUALIFIER, IMPLICIT_OBJECT_FOR_METHOD_WITH_REF_QUALIFIER, FIRST_PARAM_OF_DIRECT_COPY_CTOR, REQUIRE_DIRECT_BINDING } private static final char[] INITIALIZER_LIST_NAME = "initializer_list".toCharArray(); //$NON-NLS-1$ private static final char[] STD_NAME = "std".toCharArray(); //$NON-NLS-1$ /** * Computes the cost of an implicit conversion sequence [over.best.ics] 13.3.3.1. * The semantics of the initialization is explained in 8.5-16. * * @param target the target (parameter) type * @param exprType the source (argument) type * @param valueCat value category of the expression * @return the cost of converting from source to target * @throws DOMException */ public static Cost checkImplicitConversionSequence(IType target, IType exprType, ValueCategory valueCat, UDCMode udc, Context ctx, IASTNode point) throws DOMException { final boolean isImpliedObject= ctx == Context.IMPLICIT_OBJECT_FOR_METHOD_WITHOUT_REF_QUALIFIER || ctx == Context.IMPLICIT_OBJECT_FOR_METHOD_WITH_REF_QUALIFIER; if (isImpliedObject) udc= UDCMode.FORBIDDEN; target= getNestedType(target, TDEF); exprType= getNestedType(exprType, TDEF | REF); final IType cv1T1= getNestedType(target, TDEF | REF); final IType T1= getNestedType(cv1T1, TDEF | REF | ALLCVQ); if (target instanceof ICPPReferenceType) { ReferenceBinding refBindingType= ReferenceBinding.OTHER_REF; // [8.5.3-5] initialization of a reference final boolean isLValueRef= !((ICPPReferenceType) target).isRValueReference(); final IType cv2T2= exprType; final IType T2= getNestedType(cv2T2, TDEF | REF | ALLCVQ); refBindingType= isLValueRef ? ReferenceBinding.LVALUE_REF : ReferenceBinding.RVALUE_REF_BINDS_RVALUE; if (exprType instanceof InitializerListType) { if (isLValueRef && getCVQualifier(cv1T1) != CVQualifier.CONST) return Cost.NO_CONVERSION; Cost cost= listInitializationSequence(((InitializerListType) exprType).getEvaluation(), T1, udc, false, point); if (cost.converts()) { cost.setReferenceBinding(refBindingType); } return cost; } // If the reference is an lvalue reference and ... if (isLValueRef) { // ... the initializer expression is an lvalue (but is not a bit field) // [for overload resolution bit-fields are treated the same, error if selected as best match] if (valueCat == LVALUE || ctx == Context.IMPLICIT_OBJECT_FOR_METHOD_WITHOUT_REF_QUALIFIER) { // 13.3.3.5: For non-static member functions declared without a ref-qualifier, // an additional rule applies: // — even if the implicit object parameter is not const-qualified, an rvalue can be // bound to the parameter as long as in all other respects the argument can be // converted to the type of the implicit object parameter. // [Note: The fact that such an argument is an rvalue does not affect the ranking of // implicit conversion sequences (13.3.3.2). — end note] if (valueCat != LVALUE) refBindingType= ReferenceBinding.RVALUE_REF_BINDS_RVALUE; // ... and "cv1 T1" is reference-compatible with "cv2 T2" Cost cost= isReferenceCompatible(cv1T1, cv2T2, isImpliedObject, point); if (cost != null) { cost.setReferenceBinding(refBindingType); return cost; } } // ... or has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be // implicitly converted to an lvalue of type 'cv3 T3', where 'cv1 T1' is reference-compatible with // 'cv3 T3' (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6) // and choosing the best one through overload resolution (13.3)), if (T2 instanceof ICPPClassType && udc != UDCMode.FORBIDDEN && isReferenceRelated(T1, T2, point) < 0) { Cost cost= initializationByConversionForDirectReference(cv1T1, cv2T2, (ICPPClassType) T2, true, false, ctx, point); if (cost != null) { cost.setReferenceBinding(refBindingType); return cost; } } } // Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 // shall be const), or the reference shall be an rvalue reference. if (isLValueRef && getCVQualifier(cv1T1) != CVQualifier.CONST) { return Cost.NO_CONVERSION; } // If the initializer expression is an xvalue, class prvalue, array prvalue, or function lvalue // and 'cv1 T1' is reference-compatible with 'cv2 T2', then the reference is bound to the value // of the initializer expression (or the appropriate base class subobject). if (valueCat == ValueCategory.XVALUE || (valueCat == ValueCategory.PRVALUE && (T2 instanceof ICPPClassType || T2 instanceof IArrayType)) || (valueCat == ValueCategory.LVALUE && T2 instanceof ICPPFunctionType)) { Cost cost = isReferenceCompatible(cv1T1, cv2T2, isImpliedObject, point); if (cost != null) { cost.setReferenceBinding(refBindingType); return cost; } } // If the initializer expression has class type (i.e. T2 is a class type), where T1 is not // reference-related to T2, and can be implicitly converted to an xvalue, class prvalue, // or function lvalue of type 'cv3 T3', where 'cv1 T1' is reference-compatible with 'cv3 T3', // then the reference is bound to the result of the conversion (or the appropriate base class // subobject). If the reference is an rvalue reference and the second standard conversion // sequence of the user-defined conversion sequence includes an lvalue-to-rvalue // conversion, the program is ill-formed [this is why we pass illFormedIfLValue = true]. if (T2 instanceof ICPPClassType) { if (udc != UDCMode.FORBIDDEN && isReferenceRelated(T1, T2, point) < 0) { Cost cost= initializationByConversionForDirectReference(cv1T1, cv2T2, (ICPPClassType) T2, false, true, ctx, point); if (cost != null) { if (cost != Cost.NO_CONVERSION) { cost.setReferenceBinding(refBindingType); } return cost; } } } // Otherwise, a temporary of type 'cv1 T1' is created and initialized from the initializer // expression using the rules for a non-reference copy initialization (8.5). The reference is then // bound to the temporary. // 13.3.3.1.7 no temporary object when converting the implicit object parameter if (!isImpliedObject && ctx != Context.REQUIRE_DIRECT_BINDING) { Cost cost= nonReferenceConversion(valueCat, cv2T2, T1, udc, point); if (cost.converts()) { cost.setReferenceBinding(refBindingType); } boolean referenceRelated = isReferenceRelated(T1, T2, point) >= 0; // If T1 is reference-related to T2, cv1 shall be the same cv-qualification as, // or greater cv-qualification than, cv2. if (referenceRelated && compareQualifications(cv1T1, cv2T2) < 0) { return Cost.NO_CONVERSION; } // if T1 is reference-related to T2 and the reference is an rvalue reference, // the initializer expression shall not be an lvalue. if (referenceRelated && !isLValueRef && valueCat == ValueCategory.LVALUE) { return Cost.NO_CONVERSION; } return cost; } return Cost.NO_CONVERSION; } // Non-reference binding return nonReferenceConversion(valueCat, exprType, T1, udc, point); } /** * C++0x: 13.3.1.6 Initialization by conversion function for direct reference binding * @param needLValue don't consider conversion functions that return rvalue references * @param illFormedIfLValue make the conversion ill-formed (by returning Cost.NO_CONVERSION) * if the best match is a conversion function that returns an * lvalue reference * Note that there's a difference between returning null and returning Cost.NO_CONVERSION: * in the former case, the caller will continue trying other conversion methods. */ private static Cost initializationByConversionForDirectReference(final IType cv1T1, final IType cv2T2, final ICPPClassType T2, boolean needLValue, boolean illFormedIfLValue, Context ctx, IASTNode point) throws DOMException { ICPPMethod[] fcns= SemanticUtil.getConversionOperators(T2, point); Cost operatorCost= null; FunctionCost bestUdcCost= null; boolean ambiguousConversionOperator= false; if (fcns.length > 0 && !(fcns[0] instanceof IProblemBinding)) { for (final ICPPMethod op : fcns) { // Note: the special case of initializing a temporary to be bound to the first parameter // of a copy constructor called with a single argument in the context of direct-initialization // is (more naturally) handled here rather than in copyInitializationOfClass(). if (op.isExplicit() && ctx != Context.FIRST_PARAM_OF_DIRECT_COPY_CTOR) continue; final ICPPFunctionType ft = op.getType(); IType t= getNestedType(ft.getReturnType(), TDEF); final boolean isLValueRef= t instanceof ICPPReferenceType && !((ICPPReferenceType) t).isRValueReference(); if (needLValue && !isLValueRef) { continue; } IType implicitParameterType= CPPSemantics.getImplicitParameterType(op); Cost udcCost= isReferenceCompatible(getNestedType(implicitParameterType, TDEF | REF), cv2T2, true, point); // expression type to implicit object type if (udcCost != null) { // Make sure top-level cv-qualifiers are compared udcCost.setReferenceBinding(ReferenceBinding.LVALUE_REF); FunctionCost udcFuncCost= new FunctionCost(op, udcCost, point); int cmp= udcFuncCost.compareTo(null, bestUdcCost); if (cmp <= 0) { Cost cost= isReferenceCompatible(cv1T1, getNestedType(t, TDEF | REF), false, point); // converted to target if (cost != null) { bestUdcCost= udcFuncCost; ambiguousConversionOperator= cmp == 0; operatorCost= cost; operatorCost.setUserDefinedConversion(op); if (illFormedIfLValue && isLValueRef) { operatorCost = Cost.NO_CONVERSION; } } } } } } if (operatorCost != null && !ambiguousConversionOperator) { return operatorCost; } return null; } /** * 8.5-16 */ private static Cost nonReferenceConversion(ValueCategory valueCat, IType source, IType target, UDCMode udc, IASTNode point) throws DOMException { if (source instanceof InitializerListType) { return listInitializationSequence(((InitializerListType) source).getEvaluation(), target, udc, false, point); } IType uqTarget= SemanticUtil.getNestedType(target, TDEF | REF | CVTYPE); IType uqSource= SemanticUtil.getNestedType(source, TDEF | REF | CVTYPE); if (uqTarget instanceof ICPPClassType) { if (uqSource instanceof ICPPClassType) { // 13.3.3.1-6 Conceptual derived to base conversion int depth= calculateInheritanceDepth(uqSource, uqTarget, point); if (depth >= 0) { if (depth == 0) { return new Cost(source, target, Rank.IDENTITY); } Cost cost= new Cost(source, target, Rank.CONVERSION); cost.setInheritanceDistance(depth); return cost; } } if (udc == UDCMode.FORBIDDEN) return Cost.NO_CONVERSION; return copyInitializationOfClass(valueCat, source, (ICPPClassType) uqTarget, udc == UDCMode.DEFER, point); } if (uqSource instanceof ICPPClassType) { if (udc == UDCMode.FORBIDDEN) return Cost.NO_CONVERSION; return initializationByConversion(valueCat, source, (ICPPClassType) uqSource, target, udc == UDCMode.DEFER, point); } return checkStandardConversionSequence(uqSource, target, point); } /** * Get those fields of 'targetClass' which participate in aggregate initialization. * These are the declared fields, excluding static fields and anonymous bit-fields * ([decl.init.aggr] p6). */ static ICPPField[] getFieldsForAggregateInitialization(ICPPClassType targetClass, IASTNode point) { ICPPField[] fields = ClassTypeHelper.getDeclaredFields(targetClass, point); ICPPField[] result = fields; int j = 0; for (int i = 0; i < fields.length; ++i) { // TODO: Check for anonymous bit-fields. ICPPField doesn't currently expose whether // it's a bit-field. if (fields[i].isStatic()) { if (fields == result) { result = new ICPPField[fields.length - 1]; System.arraycopy(fields, 0, result, 0, i); } } else if (fields != result) { result[j] = fields[i]; ++j; } } return ArrayUtil.trim(result); } /** * Checks whether 'targetClass' can be initialized from 'list' according to the rules for * aggregate initialization ([dcl.init.aggr]). */ static boolean checkAggregateInitialization(EvalInitList list, ICPPClassType targetClass, IASTNode point) throws DOMException { ICPPField[] fields = getFieldsForAggregateInitialization(targetClass, point); ICPPEvaluation[] initializers = list.getClauses(); // p7: An initializer-list is ill-formed if the number of initializer-clauses exceeds // the number of members to initialize. if (initializers.length > fields.length) { return false; } // p3: The elements of the initializer list are taken as initializers for the elements // of the aggregate, in order. int i = 0; for (; i < initializers.length; ++i) { ICPPEvaluation initializer = initializers[i]; ICPPField field = fields[i]; // Each element is copy-initialized from the corresponding initializer-clause. Cost cost = checkImplicitConversionSequence(field.getType(), initializer.getType(point), initializer.getValueCategory(point), UDCMode.ALLOWED, Context.ORDINARY, point); if (!cost.converts()) { return false; } // If the initializer-clause is an expression and a narrowing conversion is // required to convert the expression, the program is ill-formed. if (!(initializer instanceof EvalInitList) && cost.isNarrowingConversion(point)) { return false; } } // p8: If there are fewer initializer-clauses than there are elements in the // aggregate, then each element not explicitly initialized shall be // initialized from its default member initializer or, if there is no // default member initializer, from an empty initializer list. for (; i < fields.length; ++i) { ICPPField field = fields[i]; IValue initialValue = field.getInitialValue(); if (initialValue != null) { continue; // has a default member initializer } // p11: If an incomplete or empty initializer-list leaves a member of // reference type uninitialized, the program is ill-formed. IType fieldType = SemanticUtil.getNestedType(field.getType(), TDEF); if (fieldType instanceof ICPPReferenceType) { return false; } // Empty initializer list EvalInitList emptyInit = new EvalInitList(ICPPEvaluation.EMPTY_ARRAY, point); Cost cost = listInitializationSequence(emptyInit, fieldType, UDCMode.ALLOWED, false, point); if (!cost.converts()) { return false; } } // TODO: Implement brace elision rules. return true; } /** * 13.3.3.1.5 List-initialization sequence [over.ics.list] */ static Cost listInitializationSequence(EvalInitList arg, IType target, UDCMode udc, boolean isDirect, IASTNode point) throws DOMException { Cost result = listInitializationSequenceHelper(arg, target, udc, isDirect, point); result.setListInitializationTarget(target); return result; } static Cost listInitializationSequenceHelper(EvalInitList arg, IType target, UDCMode udc, boolean isDirect, IASTNode point) throws DOMException { IType listType= getInitListType(target); if (listType == null && target instanceof IArrayType) { Number arraySize = ((IArrayType) target).getSize().numberValue(); if (arraySize != null) { IType elementType = ((IArrayType) target).getType(); // TODO(nathanridge): If there are fewer initializer clauses than the array size, // then the element type is required to be default-constructible. if (arg.getClauses().length <= arraySize.longValue()) { listType = elementType; } } } if (listType != null) { ICPPEvaluation[] clauses = arg.getClauses(); Cost worstCost= new Cost(arg.getType(point), target, Rank.IDENTITY); for (ICPPEvaluation clause : clauses) { Cost cost= checkImplicitConversionSequence(listType, clause.getType(point), clause.getValueCategory(point), UDCMode.ALLOWED, Context.ORDINARY, point); if (!cost.converts()) return cost; if (cost.isNarrowingConversion(point)) { cost.setRank(Rank.NO_MATCH); return cost; } if (cost.compareTo(worstCost) > 0) { worstCost= cost; } } return worstCost; } IType noCVTarget= getNestedType(target, CVTYPE | TDEF); if (noCVTarget instanceof ICPPClassType) { if (udc == UDCMode.FORBIDDEN) return Cost.NO_CONVERSION; ICPPClassType classTarget= (ICPPClassType) noCVTarget; if (TypeTraits.isAggregateClass(classTarget, point) && checkAggregateInitialization(arg, classTarget, point)) { Cost cost= new Cost(arg.getType(point), target, Rank.IDENTITY); cost.setUserDefinedConversion(null); return cost; } return listInitializationOfClass(arg, classTarget, isDirect, udc == UDCMode.DEFER, point); } ICPPEvaluation[] args = arg.getClauses(); if (args.length == 1) { final ICPPEvaluation firstArg = args[0]; if (!firstArg.isInitializerList()) { Cost cost= checkImplicitConversionSequence(target, firstArg.getType(point), firstArg.getValueCategory(point), udc, Context.ORDINARY, point); if (cost.isNarrowingConversion(point)) { return Cost.NO_CONVERSION; } return cost; } } else if (args.length == 0) { return new Cost(arg.getType(point), target, Rank.IDENTITY); } return Cost.NO_CONVERSION; } static IType getInitListType(IType target) { if (target instanceof ICPPClassType && target instanceof ICPPTemplateInstance) { ICPPTemplateInstance inst = (ICPPTemplateInstance) target; if (CharArrayUtils.equals(INITIALIZER_LIST_NAME, inst.getNameCharArray())) { IBinding owner = inst.getOwner(); if (owner instanceof ICPPNamespace && CharArrayUtils.equals(STD_NAME, owner.getNameCharArray()) && owner.getOwner() == null) { ICPPTemplateArgument[] args = inst.getTemplateArguments(); if (args.length == 1) { ICPPTemplateArgument arg = args[0]; if (arg.isTypeValue()) { return arg.getTypeValue(); } } } } } return null; } /** * [3.9.3-4] Implements cv-ness (partial) comparison. There is a (partial) * ordering on cv-qualifiers, so that a type can be said to be more * cv-qualified than another. * @return <ul> * <li>3 if cv1 == const volatile cv2 * <li>2 if cv1 == volatile cv2 * <li>1 if cv1 == const cv2 * <li>EQ 0 if cv1 == cv2 * <li>LT -1 if cv1 is less qualified than cv2 or not comparable * </ul> */ private static final int compareQualifications(IType t1, IType t2) { return getCVQualifier(t1).partialComparison(getCVQualifier(t2)); } /** * [8.5.3] "cv1 T1" is reference-related to "cv2 T2" if T1 is the same type as T2, * or T1 is a base class of T2. * Note this is not a symmetric relation. * @return inheritance distance, or -1, if <code>cv1t1</code> is not reference-related to <code>cv2t2</code> */ private static final int isReferenceRelated(IType cv1Target, IType cv2Source, IASTNode point) { IType t= SemanticUtil.getNestedType(cv1Target, TDEF | REF); IType s= SemanticUtil.getNestedType(cv2Source, TDEF | REF); // The way cv-qualification is currently modeled means // we must cope with IPointerType objects separately. if (t instanceof IPointerType) { if (s instanceof IPointerType) { t= SemanticUtil.getNestedType(((IPointerType) t).getType(), TDEF | REF); s= SemanticUtil.getNestedType(((IPointerType) s).getType(), TDEF | REF); } else { return -1; } } else if (t instanceof IArrayType) { if (s instanceof IArrayType) { final IArrayType at = (IArrayType) t; final IArrayType st = (IArrayType) s; final IValue av= at.getSize(); final IValue sv= st.getSize(); if (av == sv || (av != null && av.equals(sv))) { t= SemanticUtil.getNestedType(at.getType(), TDEF | REF | ALLCVQ); s= SemanticUtil.getNestedType(st.getType(), TDEF | REF | ALLCVQ); } else { return -1; } } else { return -1; } } else { if (t instanceof IQualifierType) t= SemanticUtil.getNestedType(((IQualifierType) t).getType(), TDEF | REF); if (s instanceof IQualifierType) s= SemanticUtil.getNestedType(((IQualifierType) s).getType(), TDEF | REF); if (t instanceof ICPPClassType && s instanceof ICPPClassType) { return SemanticUtil.calculateInheritanceDepth(s, t, point); } } if (t == s || (t != null && s != null && t.isSameType(s))) { return 0; } return -1; } /** * [8.5.3] "cv1 T1" is reference-compatible with "cv2 T2" if T1 is reference-related * to T2 and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2. * Note this is not a symmetric relation. * @return The cost for converting or <code>null</code> if <code>cv1t1</code> is not * reference-compatible with <code>cv2t2</code> */ private static final Cost isReferenceCompatible(IType cv1Target, IType cv2Source, boolean isImpliedObject, IASTNode point) { int inheritanceDist= isReferenceRelated(cv1Target, cv2Source, point); if (inheritanceDist < 0) return null; final int cmp= compareQualifications(cv1Target, cv2Source); if (cmp < 0) return null; Cost cost= new Cost(cv2Source, cv1Target, Rank.IDENTITY); cost.setQualificationAdjustment(cmp); if (inheritanceDist > 0) { cost.setInheritanceDistance(inheritanceDist); cost.setRank(Rank.CONVERSION); } if (isImpliedObject) { cost.setImpliedObject(); } return cost; } /** * [4] Standard Conversions * Computes the cost of using the standard conversion sequence from source to target. */ private static final Cost checkStandardConversionSequence(IType source, IType target, IASTNode point) { final Cost cost= new Cost(source, target, Rank.IDENTITY); if (lvalue_to_rvalue(cost)) return cost; if (promotion(cost)) return cost; if (conversion(cost, point)) return cost; if (qualificationConversion(cost)) return cost; // If we can't convert the qualifications, then we can't do anything cost.setRank(Rank.NO_MATCH); return cost; } // 13.3.1.7 Initialization by list-initialization static Cost listInitializationOfClass(EvalInitList arg, ICPPClassType t, boolean isDirect, boolean deferUDC, IASTNode point) throws DOMException { if (deferUDC) { Cost c= new Cost(arg.getType(point), t, Rank.USER_DEFINED_CONVERSION); c.setDeferredUDC(isDirect ? DeferredUDC.DIRECT_LIST_INIT_OF_CLASS : DeferredUDC.LIST_INIT_OF_CLASS); return c; } // If T has an initializer-list constructor ICPPConstructor usedCtor= null; Cost bestCost= null; boolean hasInitListConstructor= false; final ICPPConstructor[] constructors = ClassTypeHelper.getConstructors(t, point); ICPPConstructor[] ctors= constructors; for (ICPPConstructor ctor : ctors) { final int minArgCount = ctor.getRequiredArgumentCount(); if (minArgCount == 0) { if (arg.getClauses().length == 0) { Cost c= new Cost(arg.getType(point), t, Rank.IDENTITY); c.setUserDefinedConversion(ctor); return c; } } else if (minArgCount <= 1) { IType[] parTypes= ctor.getType().getParameterTypes(); if (parTypes.length > 0) { final IType target = parTypes[0]; if (getInitListType(target) != null) { hasInitListConstructor= true; Cost cost= listInitializationSequence(arg, target, UDCMode.FORBIDDEN, isDirect, point); if (cost.converts()) { int cmp= cost.compareTo(bestCost); if (bestCost == null || cmp < 0) { usedCtor= ctor; cost.setUserDefinedConversion(ctor); bestCost= cost; } else if (cmp == 0) { bestCost.setAmbiguousUDC(true); } } } } } } if (hasInitListConstructor) { if (bestCost == null) return Cost.NO_CONVERSION; if (!bestCost.isAmbiguousUDC() && !isDirect) { if (usedCtor != null && usedCtor.isExplicit()) { bestCost.setRank(Rank.NO_MATCH); } } // This cost came from listInitializationSequence() with an std::initializer_list // type as the list initialization target. From the point of view of the caller, // however, the target is the class type, not std::initializer_list, so update it // accordingly. bestCost.setListInitializationTarget(t); return bestCost; } // No initializer-list constructor LookupData data= new LookupData(t.getNameCharArray(), null, point); final ICPPEvaluation[] expandedArgs = arg.getClauses(); data.setFunctionArguments(false, expandedArgs); data.fNoNarrowing= true; // 13.3.3.1.4 ICPPConstructor[] filteredConstructors = constructors; if (expandedArgs.length == 1) { filteredConstructors= new ICPPConstructor[constructors.length]; int j= 0; for (ICPPConstructor ctor : constructors) { if (ctor.getRequiredArgumentCount() < 2) { IType[] ptypes= ctor.getType().getParameterTypes(); if (ptypes.length > 0) { IType ptype= getNestedType(ptypes[0], TDEF | REF | CVTYPE); if (!t.isSameType(ptype)) { filteredConstructors[j++]= ctor; } } } } } final IBinding result= CPPSemantics.resolveFunction(data, filteredConstructors, true); final Cost c; if (result instanceof ICPPMethod) { c= new Cost(arg.getType(point), t, Rank.IDENTITY); c.setUserDefinedConversion((ICPPMethod) result); } else if (result instanceof IProblemBinding && ((IProblemBinding) result).getID() == IProblemBinding.SEMANTIC_AMBIGUOUS_LOOKUP) { c = new Cost(arg.getType(point), t, Rank.USER_DEFINED_CONVERSION); c.setAmbiguousUDC(true); } else { c= Cost.NO_CONVERSION; } // This cost came from listInitializationSequence() with an std::initializer_list // type as the list initialization target. From the point of view of the caller, // however, the target is the class type, not std::initializer_list, so update it // accordingly. c.setListInitializationTarget(t); return c; } /** * 13.3.1.4 Copy-initialization of class by user-defined conversion [over.match.copy] */ static final Cost copyInitializationOfClass(ValueCategory valueCat, IType source, ICPPClassType t, boolean deferUDC, IASTNode point) throws DOMException { if (deferUDC) { Cost c= new Cost(source, t, Rank.USER_DEFINED_CONVERSION); c.setDeferredUDC(DeferredUDC.COPY_INIT_OF_CLASS); return c; } FunctionCost cost1= null; Cost cost2= null; ICPPFunction[] ctors= ClassTypeHelper.getConstructors(t, point); ctors = CPPTemplates.instantiateForFunctionCall(ctors, null, Collections.singletonList(source), Collections.singletonList(valueCat), false, point); for (ICPPFunction f : ctors) { if (!(f instanceof ICPPConstructor) || f instanceof IProblemBinding) continue; ICPPConstructor ctor= (ICPPConstructor) f; // Note: the special case of initializing a temporary to be bound to the first parameter // of a copy constructor called with a single argument in the context of direct-initialization // is (more naturally) handled in initializationByConversionForDirectReference. if (!ctor.isExplicit()) { final ICPPFunctionType ft = ctor.getType(); final IType[] ptypes = ft.getParameterTypes(); FunctionCost c1; if (ptypes.length == 0) { if (ctor.takesVarArgs()) { c1= new FunctionCost(ctor, new Cost(source, null, Rank.ELLIPSIS_CONVERSION), point); } else { continue; } } else { IType ptype= SemanticUtil.getNestedType(ptypes[0], TDEF); // We don't need to check the implicit conversion sequence if the type is void if (SemanticUtil.isVoidType(ptype)) continue; if (ctor.getRequiredArgumentCount() > 1) continue; c1= new FunctionCost(ctor, checkImplicitConversionSequence(ptype, source, valueCat, UDCMode.FORBIDDEN, Context.ORDINARY, point), point); } int cmp= c1.compareTo(null, cost1); if (cmp <= 0) { cost1= c1; cost2= new Cost(t, t, Rank.IDENTITY); cost2.setUserDefinedConversion(ctor); if (cmp == 0) { cost2.setAmbiguousUDC(true); } } } } final IType uqSource= getNestedType(source, TDEF | REF | CVTYPE); if (uqSource instanceof ICPPClassType) { ICPPFunction[] ops = SemanticUtil.getConversionOperators((ICPPClassType) uqSource, point); ops= CPPTemplates.instantiateConversionTemplates(ops, t, point); for (final ICPPFunction f : ops) { if (f instanceof ICPPMethod && !(f instanceof IProblemBinding)) { ICPPMethod op= (ICPPMethod) f; if (op.isExplicit()) continue; final IType returnType = op.getType().getReturnType(); final IType uqReturnType= getNestedType(returnType, REF | TDEF | CVTYPE); final int dist = SemanticUtil.calculateInheritanceDepth(uqReturnType, t, point); if (dist >= 0) { IType implicitType= CPPSemantics.getImplicitParameterType(op); final Cost udcCost = isReferenceCompatible(getNestedType(implicitType, TDEF | REF), source, true, point); if (udcCost != null) { // Make sure top-level cv-qualifiers are compared udcCost.setReferenceBinding(ReferenceBinding.LVALUE_REF); FunctionCost c1= new FunctionCost(op, udcCost, point); int cmp= c1.compareTo(null, cost1); if (cmp <= 0) { cost1= c1; cost2= new Cost(t, t, Rank.IDENTITY); if (dist > 0) { cost2.setInheritanceDistance(dist); cost2.setRank(Rank.CONVERSION); } cost2.setUserDefinedConversion(op); if (cmp == 0) { cost2.setAmbiguousUDC(true); } } } } } } } if (cost1 == null || !cost1.getCost(0).converts()) return Cost.NO_CONVERSION; return cost2; } /** * 13.3.1.5 Initialization by conversion function [over.match.conv] */ static Cost initializationByConversion(ValueCategory valueCat, IType source, ICPPClassType uqSource, IType target, boolean deferUDC, IASTNode point) throws DOMException { if (deferUDC) { Cost c= new Cost(source, target, Rank.USER_DEFINED_CONVERSION); c.setDeferredUDC(DeferredUDC.INIT_BY_CONVERSION); return c; } ICPPFunction[] ops = SemanticUtil.getConversionOperators(uqSource, point); ops= CPPTemplates.instantiateConversionTemplates(ops, target, point); FunctionCost cost1= null; Cost cost2= null; for (final ICPPFunction f : ops) { if (f instanceof ICPPMethod && !(f instanceof IProblemBinding)) { ICPPMethod op= (ICPPMethod) f; final boolean isExplicitConversion= op.isExplicit(); if (isExplicitConversion /** && !direct **/) continue; ICPPFunctionType functionType = op.getType(); final IType returnType = functionType.getReturnType(); IType uqReturnType= getNestedType(returnType, TDEF | ALLCVQ); Cost c2= checkImplicitConversionSequence(target, uqReturnType, valueCategoryFromReturnType(uqReturnType), UDCMode.FORBIDDEN, Context.ORDINARY, point); if (c2.converts()) { if (isExplicitConversion && c2.getRank() != Rank.IDENTITY) continue; IType implicitType= CPPSemantics.getImplicitParameterType(op); final Cost udcCost = isReferenceCompatible(getNestedType(implicitType, TDEF | REF), source, true, point); if (udcCost != null) { // Make sure top-level cv-qualifiers are compared if (functionType.hasRefQualifier() && functionType.isRValueReference()) { udcCost.setReferenceBinding(ReferenceBinding.RVALUE_REF_BINDS_RVALUE); } else { udcCost.setReferenceBinding(ReferenceBinding.LVALUE_REF); } FunctionCost c1= new FunctionCost(op, udcCost, point); int cmp= c1.compareTo(null, cost1); if (cmp <= 0) { cost1= c1; cost2= c2; cost2.setUserDefinedConversion(op); if (cmp == 0) { cost2.setAmbiguousUDC(true); } } } } } } if (cost1 == null || !cost1.getCost(0).converts()) return Cost.NO_CONVERSION; return cost2; } /** * Attempts the conversions below and returns whether this completely converts the source to * the target type. * [4.1] Lvalue-to-rvalue conversion * [4.2] array-to-ptr * [4.3] function-to-ptr */ private static final boolean lvalue_to_rvalue(final Cost cost) { IType target = getNestedType(cost.target, REF | TDEF | ALLCVQ); IType source= getNestedType(cost.source, REF | TDEF); // 4.2 array to pointer conversion if (source instanceof IArrayType) { if (target instanceof IPointerType) { // 4.2-2 a string literal can be converted to pointer to char source = unqualifyStringLiteral(source, (IPointerType) target, cost); } if (!(source instanceof IPointerType)) { source = new CPPPointerType(getNestedType(((IArrayType) source).getType(), TDEF)); } } else if (source instanceof IFunctionType) { // 4.3 function to pointer conversion source = new CPPPointerType(source); } else { if (source instanceof IPointerType) { // A string literal may have been converted to a pointer when // computing the type of a conditional expression. if (target instanceof IPointerType) { // 4.2-2 a string literal can be converted to pointer to char source = unqualifyStringLiteral(source, (IPointerType) target, cost); } } source = getNestedType(source, TDEF | REF | ALLCVQ); } if (source == null || target == null) { cost.setRank(Rank.NO_MATCH); return true; } cost.source= source; cost.target= target; return source.isSameType(target); } private static IType unqualifyStringLiteral(IType source, final IPointerType target, final Cost cost) { if (target instanceof ICPPPointerToMemberType) return source; final IType targetPtrTgt= getNestedType((target).getType(), TDEF); if (targetPtrTgt instanceof IQualifierType && ((IQualifierType) targetPtrTgt).isConst()) return source; IType srcTarget= ((ITypeContainer) source).getType(); if (!(srcTarget instanceof IQualifierType)) return source; final IQualifierType srcQTarget= (IQualifierType) srcTarget; if (srcQTarget.isConst() && !srcQTarget.isVolatile()) { srcTarget= srcQTarget.getType(); if (srcTarget instanceof CPPBasicType) { if (((CPPBasicType) srcTarget).isFromStringLiteral()) { source= new CPPPointerType(srcTarget, false, false, false); CVQualifier cvqTarget = getCVQualifier(targetPtrTgt).add(CVQualifier.CONST); cost.setQualificationAdjustment(cvqTarget.partialComparison(CVQualifier.NONE) << 3); } } } return source; } /** * [4.4] Qualifications * @param cost */ private static final boolean qualificationConversion(Cost cost) { IType s = cost.source; IType t = cost.target; boolean constInEveryCV2k = true; boolean firstPointer= true; int adjustments= 0; int shift=0; while (true) { s= getNestedType(s, TDEF | REF); t= getNestedType(t, TDEF | REF); if (s instanceof IPointerType && t instanceof IPointerType) { final int cmp= compareQualifications(t, s); // is t more qualified than s? if (cmp < 0 || (cmp > 0 && !constInEveryCV2k)) { return false; } final IPointerType tPtr = (IPointerType) t; final IPointerType sPtr = (IPointerType) s; if (haveMemberPtrConflict(sPtr, tPtr)) return false; constInEveryCV2k &= (firstPointer || tPtr.isConst()); s= sPtr.getType(); t= tPtr.getType(); firstPointer= false; adjustments |= (cmp << shift); shift+= 3; } else { break; } } int cmp= compareQualifications(t, s); // is t more qualified than s? if (cmp < 0 || (cmp > 0 && !constInEveryCV2k)) { return false; } adjustments |= (cmp << shift); s= getNestedType(s, ALLCVQ | TDEF | REF); t= getNestedType(t, ALLCVQ | TDEF | REF); if (adjustments > 0) { cost.setQualificationAdjustment(adjustments); } return s != null && t != null && s.isSameType(t); } private static boolean haveMemberPtrConflict(IPointerType s, IPointerType t) { final boolean sIsPtrToMember = s instanceof ICPPPointerToMemberType; final boolean tIsPtrToMember = t instanceof ICPPPointerToMemberType; if (sIsPtrToMember != tIsPtrToMember) { return true; } if (sIsPtrToMember) { final IType sMemberOf = ((ICPPPointerToMemberType) s).getMemberOfClass(); final IType tMemberOf = ((ICPPPointerToMemberType) t).getMemberOfClass(); if (sMemberOf == null || tMemberOf == null || !sMemberOf.isSameType(tMemberOf)) { return true; } } return false; } /** * Attempts promotions and returns whether the promotion converted the type. * * [4.5] [4.6] Promotion * * 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 */ private static final boolean promotion(Cost cost) { IType src = cost.source; IType trg = cost.target; boolean canPromote= false; if (trg instanceof IBasicType) { IBasicType basicTgt = (IBasicType) trg; final Kind tKind = basicTgt.getKind(); if (src instanceof ICPPEnumeration) { final ICPPEnumeration enumType = (ICPPEnumeration) src; if (enumType.isScoped()) { return false; } IType fixedType= enumType.getFixedType(); if (fixedType == null) { if (tKind == Kind.eInt || tKind == Kind.eUnspecified) { if (trg instanceof ICPPBasicType) { int qualifiers = ArithmeticConversion.getEnumIntTypeModifiers((IEnumeration) src); int targetModifiers = ((ICPPBasicType) trg).getModifiers(); if (qualifiers == (targetModifiers & (IBasicType.IS_LONG | IBasicType.IS_LONG_LONG | IBasicType.IS_SHORT | IBasicType.IS_UNSIGNED))) { canPromote = true; } } else { canPromote = true; } } } else { if (fixedType.isSameType(trg)) canPromote= true; // Allow to further promote the fixed type src= fixedType; } } if (src instanceof IBasicType) { final IBasicType basicSrc = (IBasicType) src; Kind sKind = basicSrc.getKind(); if (tKind == Kind.eInt) { if (!basicTgt.isLong() && !basicTgt.isLongLong() && !basicTgt.isShort()) { switch (sKind) { case eInt: // short, and unsigned short if (basicSrc.isShort() && !basicTgt.isUnsigned()) { canPromote= true; } break; case eChar: case eBoolean: case eWChar: case eChar16: case eUnspecified: // treat unspecified as int if (!basicTgt.isUnsigned()) { canPromote= true; } break; case eChar32: if (basicTgt.isUnsigned()) { canPromote= true; } break; default: break; } } } else if (tKind == Kind.eDouble && sKind == Kind.eFloat) { canPromote= true; } } } if (canPromote) { cost.setRank(Rank.PROMOTION); return true; } return false; } /** * Attempts conversions and returns whether the conversion succeeded. * [4.7] Integral conversions * [4.8] Floating point conversions * [4.9] Floating-integral conversions * [4.10] Pointer conversions * [4.11] Pointer to member conversions */ private static final boolean conversion(Cost cost, IASTNode point) { final IType s = cost.source; final IType t = cost.target; if (t instanceof IBasicType) { // 4.7 integral conversion // 4.8 floating point conversion // 4.9 floating-integral conversion final Kind tgtKind = ((IBasicType) t).getKind(); if (s instanceof IBasicType) { final Kind srcKind = ((IBasicType) s).getKind(); if (srcKind == Kind.eVoid) return false; // 4.12 std::nullptr_t can be converted to bool if (srcKind == Kind.eNullPtr && tgtKind != Kind.eBoolean) return false; // 4.10-1 a null pointer constant can be converted to std::nullptr_t if (tgtKind == Kind.eNullPtr && !isNullPointerConstant(s)) return false; cost.setRank(Rank.CONVERSION); if (srcKind != Kind.eNullPtr && tgtKind != Kind.eNullPtr) { cost.setCouldNarrow(); } return true; } if (s instanceof ICPPEnumeration && !((ICPPEnumeration) s).isScoped()) { // 4.7 An rvalue of an enumeration type can be converted to an rvalue of an integer type. cost.setRank(Rank.CONVERSION); cost.setCouldNarrow(); return true; } // 4.12 pointer or pointer to member type can be converted to an rvalue of type bool if (tgtKind == Kind.eBoolean && s instanceof IPointerType) { cost.setRank(Rank.CONVERSION_PTR_BOOL); return true; } } if (t instanceof IPointerType) { IPointerType tgtPtr= (IPointerType) t; // 4.10-1 an integral constant expression of integer type that evaluates to 0 can // be converted to a pointer type // 4.11-1 same for pointer to member if (s instanceof IBasicType) { if (isNullPointerConstant(s)) { cost.setRank(Rank.CONVERSION); return true; } return false; } if (s instanceof IPointerType) { IPointerType srcPtr= (IPointerType) s; // 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" IType tgtPtrTgt= getNestedType(tgtPtr.getType(), TDEF | CVTYPE | REF); if (SemanticUtil.isVoidType(tgtPtrTgt)) { cost.setRank(Rank.CONVERSION); cost.setInheritanceDistance(Short.MAX_VALUE); CVQualifier cv= getCVQualifier(srcPtr.getType()); cost.source= new CPPPointerType(addQualifiers(CPPSemantics.VOID_TYPE, cv.isConst(), cv.isVolatile(), cv.isRestrict())); return false; } final boolean tIsPtrToMember = t instanceof ICPPPointerToMemberType; final boolean sIsPtrToMember = s instanceof ICPPPointerToMemberType; if (!tIsPtrToMember && !sIsPtrToMember) { // 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. IType srcPtrTgt= getNestedType(srcPtr.getType(), TDEF | CVTYPE | REF); if (tgtPtrTgt instanceof ICPPClassType && srcPtrTgt instanceof ICPPClassType) { int depth= SemanticUtil.calculateInheritanceDepth(srcPtrTgt, tgtPtrTgt, point); if (depth == -1) { cost.setRank(Rank.NO_MATCH); return true; } if (depth > 0) { cost.setRank(Rank.CONVERSION); cost.setInheritanceDistance(depth); CVQualifier cv= getCVQualifier(srcPtr.getType()); cost.source= new CPPPointerType(addQualifiers(tgtPtrTgt, cv.isConst(), cv.isVolatile(), cv.isRestrict())); } return false; } } else if (tIsPtrToMember && sIsPtrToMember) { // 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 ICPPPointerToMemberType spm = (ICPPPointerToMemberType) s; ICPPPointerToMemberType tpm = (ICPPPointerToMemberType) t; IType st = spm.getType(); IType tt = tpm.getType(); if (st != null && tt != null && st.isSameType(tt)) { int depth = SemanticUtil.calculateInheritanceDepth(tpm.getMemberOfClass(), spm.getMemberOfClass(), point); if (depth == -1) { cost.setRank(Rank.NO_MATCH); return true; } if (depth > 0) { cost.setRank(Rank.CONVERSION); cost.setInheritanceDistance(depth); cost.source = new CPPPointerToMemberType(spm.getType(), tpm.getMemberOfClass(), spm.isConst(), spm.isVolatile(), spm.isRestrict()); } return false; } } } } return false; } public static boolean isNullPointerConstant(IType s) { if (s instanceof CPPBasicType) { final CPPBasicType basicType = (CPPBasicType) s; if (basicType.getKind() == Kind.eNullPtr) return true; Long val = basicType.getAssociatedNumericalValue(); if (val != null && val == 0) { return true; } } return false; } /** * 4.1, 4.2, 4.3 */ public static IType lvalue_to_rvalue(IType type, boolean resolveTypedefs) { IType t= SemanticUtil.getNestedType(type, TDEF | REF); if (t instanceof IArrayType) { return new CPPPointerType(((IArrayType) t).getType()); } if (t instanceof IFunctionType) { return new CPPPointerType(t); } IType uqType= SemanticUtil.getNestedType(t, TDEF | REF | ALLCVQ); if (uqType instanceof ICPPClassType) { return resolveTypedefs ? t : SemanticUtil.getNestedType(type, COND_TDEF | REF); } return resolveTypedefs ? uqType : SemanticUtil.getNestedType(type, COND_TDEF | REF | ALLCVQ); } /** * Composite pointer type computed as described in 5.9-2 except that if the conversion to * the pointer is not possible, the method returns {@code null}. */ public static IType compositePointerType(IType t1, IType t2, IASTNode point) { t1 = SemanticUtil.getNestedType(t1, TDEF); t2 = SemanticUtil.getNestedType(t2, TDEF); final boolean isPtr1 = t1 instanceof IPointerType; if (isPtr1 || isNullPtr(t1)) { if (isNullPointerConstant(t2)) { return t1; } } final boolean isPtr2 = t2 instanceof IPointerType; if (isPtr2 || isNullPtr(t2)) { if (isNullPointerConstant(t1)) { return t2; } } if (!isPtr1 || !isPtr2) return null; final IPointerType p1= (IPointerType) t1; final IPointerType p2= (IPointerType) t2; if (haveMemberPtrConflict(p1, p2)) return null; final IType target1 = p1.getType(); if (isVoidType(target1)) { return addQualifiers(p1, p2.isConst(), p2.isVolatile(), p2.isRestrict()); } final IType target2 = p2.getType(); if (isVoidType(target2)) { return addQualifiers(p2, p1.isConst(), p1.isVolatile(), p1.isRestrict()); } IType t= mergePointers(target1, target2, point, true, true); if (t == null) return null; if (t == target1) return p1; if (t == target2) return p2; return copyPointer(p1, t, false, false); } private static IType mergePointers(IType t1, IType t2, IASTNode point, boolean allcq, boolean allowInheritance) { t1= getNestedType(t1, TDEF | REF); t2= getNestedType(t2, TDEF | REF); if (t1 instanceof IPointerType && t2 instanceof IPointerType) { final IPointerType p1 = (IPointerType) t1; final IPointerType p2 = (IPointerType) t2; final CVQualifier cv1= getCVQualifier(t1); final CVQualifier cv2= getCVQualifier(t2); if (haveMemberPtrConflict(p1, p2)) return null; if (!allcq && cv1 != cv2) return null; final IType p1target = p1.getType(); IType merged= mergePointers(p1target, p2.getType(), point, allcq && (cv1.isConst() || cv2.isConst()), false); if (merged == null) return null; if (p1target == merged && cv1.isAtLeastAsQualifiedAs(cv2)) return p1; if (p2.getType() == merged && cv2.isAtLeastAsQualifiedAs(cv1)) return p2; return copyPointer(p1, merged, cv1.isConst() || cv2.isConst(), cv1.isVolatile() || cv2.isVolatile()); } final IType uq1= getNestedType(t1, TDEF|REF|CVTYPE); final IType uq2= getNestedType(t2, TDEF|REF|CVTYPE); if (uq1 == null) { return null; } if (uq1.isSameType(uq2)) { if (uq1 == t1 && uq2 == t2) return t1; CVQualifier cv1= getCVQualifier(t1); CVQualifier cv2= getCVQualifier(t2); if (cv1 == cv2) return t1; if (!allcq) return null; if (cv1.isAtLeastAsQualifiedAs(cv2)) return t1; if (cv2.isAtLeastAsQualifiedAs(cv1)) return t2; // One type is const the other is volatile. return new CPPQualifierType(uq1, true, true); } else if (allowInheritance) { // Allow for conversion from pointer-to-derived to pointer-to-base as per [conv.ptr] p3. IType base; if (SemanticUtil.calculateInheritanceDepth(uq1, uq2, point) > 0) { base = uq2; } else if (SemanticUtil.calculateInheritanceDepth(uq2, uq1, point) > 0) { base = uq1; } else { return null; } CVQualifier cv1= getCVQualifier(t1); CVQualifier cv2= getCVQualifier(t2); if (cv1 == CVQualifier.NONE && cv2 == CVQualifier.NONE) { return base; } return new CPPQualifierType(base, cv1.isConst() || cv2.isConst(), cv1.isVolatile() || cv2.isVolatile()); } return null; } public static IType copyPointer(final IPointerType p1, IType target, final boolean isConst, final boolean isVolatile) { if (p1 instanceof ICPPPointerToMemberType) { ICPPPointerToMemberType ptm= (ICPPPointerToMemberType) p1; return new CPPPointerToMemberType(target, ptm.getMemberOfClass(), isConst, isVolatile, false); } return new CPPPointerType(target, isConst, isVolatile, false); } private static boolean isNullPtr(IType t1) { return t1 instanceof IBasicType && ((IBasicType) t1).getKind() == Kind.eNullPtr; } }