/*******************************************************************************
* Copyright (c) 2004, 2010 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)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
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.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.Value;
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;
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;
// 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() {
if (fCouldNarrow) {
if (source instanceof CPPBasicType && target instanceof ICPPBasicType) {
ICPPBasicType basicTarget= (ICPPBasicType) target;
final Kind targetKind = basicTarget.getKind();
if (targetKind != Kind.eInt && targetKind != Kind.eFloat && targetKind != Kind.eDouble) {
return true;
}
IASTExpression val = ((CPPBasicType) source).getCreatedFromExpression();
if (val instanceof IASTLiteralExpression) {
// mstodo extend to constant expressions
Long l= Value.create(val, Value.MAX_RECURSION_DEPTH).numericalValue();
if (l != null) {
long n= l.longValue();
return !ArithmeticConversion.fitsIntoType(basicTarget, n);
}
}
}
return true;
}
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;
}
}