/*******************************************************************************
* Copyright (c) 2004, 2016 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)
* Nathan Ridge
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBasicType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.internal.core.dom.parser.ArithmeticConversion;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
/**
* The cost of an implicit conversion sequence.
*
* See [over.best.ics] 13.3.3.1.
*/
public class Cost {
public enum DeferredUDC {
NONE, COPY_INIT_OF_CLASS, INIT_BY_CONVERSION, LIST_INIT_OF_CLASS, DIRECT_LIST_INIT_OF_CLASS
}
public enum Rank {
IDENTITY, PROMOTION, CONVERSION, CONVERSION_PTR_BOOL,
USER_DEFINED_CONVERSION, ELLIPSIS_CONVERSION, NO_MATCH
}
enum ReferenceBinding {
RVALUE_REF_BINDS_RVALUE, LVALUE_REF, OTHER_REF, NO_REF
}
public static final Cost NO_CONVERSION = new Cost(null, null, Rank.NO_MATCH) {
@Override
public void setRank(Rank rank) {
assert false;
}
@Override
public void setReferenceBinding(ReferenceBinding binding) {
assert false;
}
@Override
public void setAmbiguousUDC(boolean val) {
assert false;
}
@Override
public void setDeferredUDC(DeferredUDC val) {
assert false;
}
@Override
public void setInheritanceDistance(int inheritanceDistance) {
assert false;
}
@Override
public void setQualificationAdjustment(int adjustment) {
assert false;
}
@Override
public void setUserDefinedConversion(ICPPMethod conv) {
assert false;
}
@Override
public void setCouldNarrow() {
assert false;
}
@Override
public void setSelectedFunction(ICPPFunction function) {
assert false;
}
};
IType source;
IType target;
private Rank fRank;
private Rank fSecondStandardConversionRank;
private boolean fAmbiguousUDC;
private DeferredUDC fDeferredUDC= DeferredUDC.NONE;
private int fQualificationAdjustments;
private int fInheritanceDistance;
private boolean fImpliedObject;
private ICPPFunction fUserDefinedConversion;
private ReferenceBinding fReferenceBinding;
private boolean fCouldNarrow;
// For a list-initialization sequence, 'target' is not always the original
// target type. Specifically, for an original target type of
// std::initializer_list<T> or array of T, 'target' will be T, but we need
// to know the original target as well so we store it here.
// This will be null iff. this is not a list-initialization sequence.
private IType fListInitializationTarget;
private ICPPFunction fSelectedFunction; // For targeted functions
public Cost(IType s, IType t, Rank rank) {
source = s;
target = t;
fRank= rank;
fReferenceBinding= ReferenceBinding.NO_REF;
}
public final Rank getRank() {
return fRank;
}
public final boolean converts() {
return fRank != Rank.NO_MATCH;
}
public void setRank(Rank rank) {
fRank= rank;
}
public ReferenceBinding getReferenceBinding() {
return fReferenceBinding;
}
public void setReferenceBinding(ReferenceBinding binding) {
fReferenceBinding= binding;
}
public boolean isAmbiguousUDC() {
return fAmbiguousUDC;
}
public void setAmbiguousUDC(boolean val) {
fAmbiguousUDC= val;
}
public DeferredUDC isDeferredUDC() {
return fDeferredUDC;
}
public void setDeferredUDC(DeferredUDC udc) {
fDeferredUDC= udc;
}
public int getInheritanceDistance() {
return fInheritanceDistance;
}
public void setInheritanceDistance(int inheritanceDistance) {
fInheritanceDistance = inheritanceDistance;
}
public void setQualificationAdjustment(int adjustment) {
fQualificationAdjustments= adjustment;
}
/**
* Converts the cost for the second standard conversion into the overall cost for the
* implicit conversion sequence.
*/
public void setUserDefinedConversion(ICPPMethod conv) {
fUserDefinedConversion= conv;
fSecondStandardConversionRank= fRank;
fRank= Rank.USER_DEFINED_CONVERSION;
fCouldNarrow= false;
}
/**
* Returns an integer < 0 if other cost is <code>null</code>, or this cost is smaller than the other cost,
* 0 if this cost is equal to the other cost,
* an integer > 0 if this cost is larger than the other cost.
*/
public int compareTo(Cost other) {
if (other == null)
return -1;
// cannot compare costs with deferred user defined conversions
assert fDeferredUDC == DeferredUDC.NONE && other.fDeferredUDC == DeferredUDC.NONE;
// 7.3.3.13 (using declarations in classes):
// for overload resolution the implicit this pointer
// is treated as if it were a pointer to the derived class
final boolean ignoreInheritanceDist= fImpliedObject && other.fImpliedObject;
Rank rank = fRank;
Rank otherRank = other.fRank;
if (ignoreInheritanceDist) {
if (rank == Rank.CONVERSION)
rank= Rank.IDENTITY;
if (otherRank == Rank.CONVERSION)
otherRank= Rank.IDENTITY;
}
int cmp= rank.compareTo(otherRank);
if (cmp != 0)
return cmp;
// [over.ics.rank] p3.3:
// List-initialization sequence L1 is a better conversion sequence than
// list-initialization sequence L2 if
if (fListInitializationTarget != null && other.fListInitializationTarget != null) {
// - L1 converts to std::initializer_list<X> for some X and L2 does not,
// or if not that,
IType initListType = Conversions.getInitListType(fListInitializationTarget);
IType otherInitListType = Conversions.getInitListType(other.fListInitializationTarget);
if (initListType != null && otherInitListType == null) {
return -1;
} else if (initListType == null && otherInitListType != null) {
return 1;
}
// - L1 converts to type "array of N1 T", L2 converts to type "array of
// N2 T", and N1 is smaller than N2
if (fListInitializationTarget instanceof IArrayType && other.fListInitializationTarget instanceof IArrayType) {
IArrayType arrayType = (IArrayType) fListInitializationTarget;
IArrayType otherArrayType = (IArrayType) other.fListInitializationTarget;
if (arrayType.getType().isSameType(otherArrayType.getType())) {
Number size = arrayType.getSize().numberValue();
Number otherSize = otherArrayType.getSize().numberValue();
if (size != null && otherSize != null) {
Long a = size.longValue();
Long b = otherSize.longValue();
return a.compareTo(b);
}
}
}
}
// rank is equal
if (rank == Rank.USER_DEFINED_CONVERSION) {
// 13.3.3.1.10
if (isAmbiguousUDC() || other.isAmbiguousUDC())
return 0;
if (fUserDefinedConversion != other.fUserDefinedConversion) {
if (fUserDefinedConversion == null ||
!fUserDefinedConversion.equals(other.fUserDefinedConversion))
return 0;
}
cmp= fSecondStandardConversionRank.compareTo(other.fSecondStandardConversionRank);
if (cmp != 0)
return cmp;
}
if (!ignoreInheritanceDist) {
cmp= fInheritanceDistance - other.fInheritanceDistance;
if (cmp != 0)
return cmp;
}
if (fReferenceBinding == ReferenceBinding.LVALUE_REF) {
if (other.fReferenceBinding == ReferenceBinding.RVALUE_REF_BINDS_RVALUE)
return 1;
} else if (fReferenceBinding == ReferenceBinding.RVALUE_REF_BINDS_RVALUE) {
if (other.fReferenceBinding == ReferenceBinding.LVALUE_REF)
return -1;
}
// Top level cv-qualifiers are compared only for reference bindings.
int qdiff= fQualificationAdjustments ^ other.fQualificationAdjustments;
if (fReferenceBinding == ReferenceBinding.NO_REF || other.fReferenceBinding == ReferenceBinding.NO_REF)
qdiff &= ~7;
if (qdiff != 0) {
if ((fQualificationAdjustments & qdiff) == 0)
return -1;
if ((other.fQualificationAdjustments & qdiff) == 0)
return 1;
}
return 0;
}
@SuppressWarnings("nls")
@Override
public String toString() {
StringBuilder buf= new StringBuilder();
String comma= "";
buf.append(fRank).append('[');
if (fQualificationAdjustments != 0) {
buf.append(comma).append("qualification=").append(fQualificationAdjustments);
comma= ", ";
}
if (fInheritanceDistance != 0) {
buf.append(comma).append("inheritance=").append(fInheritanceDistance);
comma= ", ";
}
if (fDeferredUDC != DeferredUDC.NONE) {
buf.append(comma).append(fDeferredUDC);
comma= ", ";
}
if (fAmbiguousUDC) {
buf.append(comma).append("ambiguous UDC");
comma= ", ";
}
if (fSecondStandardConversionRank != null) {
buf.append(comma).append("2ndConvRank=").append(fSecondStandardConversionRank);
}
buf.append(']');
return buf.toString();
}
public boolean isNarrowingConversion(IASTNode point) {
if (!fCouldNarrow)
return false;
// Determine whether this is a narrowing conversion, according to 8.5.4/7 (dcl.list.init).
if (!(target instanceof ICPPBasicType))
return false;
ICPPBasicType basicTarget = (ICPPBasicType) target;
// Deal with an enumeration source type.
// If it has a fixed underlying type, treat it as if it were that underlying type.
// If not, check whether the target type can represent its min and max values.
CPPBasicType basicSource = null;
if (source instanceof CPPBasicType) {
basicSource = (CPPBasicType) source;
} else if (source instanceof IEnumeration) {
IEnumeration enumSource = (IEnumeration) source;
if (enumSource instanceof ICPPEnumeration) {
IType fixedType = ((ICPPEnumeration) enumSource).getFixedType();
if (fixedType instanceof CPPBasicType) {
basicSource = (CPPBasicType) fixedType;
}
}
if (basicSource == null) { // C enumeration or no fixed type
return !ArithmeticConversion.fitsIntoType(basicTarget, enumSource.getMinValue()) ||
!ArithmeticConversion.fitsIntoType(basicTarget, enumSource.getMaxValue());
}
}
if (basicSource == null)
return false;
// The standard provides for an exception in some cases where, based on the types only,
// a conversion would be narrowing, but the source expression is a constant-expression
// and its value is exactly representable by the target type.
boolean constantExprExceptionApplies = false;
if (BuiltinOperators.isFloatingPoint(basicSource) && BuiltinOperators.isIntegral(basicTarget)) {
// From a floating-point type to an integer type
return true;
} else if (basicSource.getKind() == Kind.eDouble
&& (basicTarget.getKind() == Kind.eFloat
|| (basicTarget.getKind() == Kind.eDouble && !basicTarget.isLong() && basicSource.isLong()))) {
// From long double to double or float, or from double to float
constantExprExceptionApplies = true;
} else if (BuiltinOperators.isIntegral(basicSource) && BuiltinOperators.isFloatingPoint(basicTarget)) {
// From an integer type or unscoped enumeration type to a floating-point type
constantExprExceptionApplies = true;
} else if (BuiltinOperators.isIntegral(basicSource)
&& BuiltinOperators.isIntegral(basicTarget)
&& !ArithmeticConversion.fitsIntoType(basicTarget, basicSource, point)) {
// From an integer type or unscoped enumeration type to an integer type that
// cannot represent all the values of the original type
constantExprExceptionApplies = true;
}
if (constantExprExceptionApplies) {
Long val = basicSource.getAssociatedNumericalValue();
return val == null || !ArithmeticConversion.fitsIntoType(basicTarget, val.longValue());
}
return false;
}
public void setCouldNarrow() {
fCouldNarrow= true;
}
public ICPPFunction getUserDefinedConversion() {
return fUserDefinedConversion;
}
/**
* Stores a selected function. Used when resolving targeted functions.
*/
public void setSelectedFunction(ICPPFunction function) {
fSelectedFunction= function;
}
public ICPPFunction getSelectedFunction() {
return fSelectedFunction;
}
public void setImpliedObject() {
fImpliedObject= true;
}
public void setListInitializationTarget(IType target) {
fListInitializationTarget = target;
}
}