1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +02:00

Bug 302412: List initialization with elements of the initializer list.

This commit is contained in:
Markus Schorn 2010-03-12 11:53:43 +00:00
parent bd8092d830
commit ce38a4facd
8 changed files with 274 additions and 57 deletions

View file

@ -8140,5 +8140,78 @@ public class AST2CPPTests extends AST2BaseTest {
bh.assertProblem("f( {1.0} )", 1);
bh.assertNonProblem("g({ \"foo\", \"bar\" })", 1);
}
// namespace std {
// template<typename T> class initializer_list;
// }
// struct str {
// str(const char*) {
// }
// };
// struct A {
// A(std::initializer_list<int>);
// };
// struct B {
// B(int, double);
// };
// struct C {
// C(str);
// };
// struct D {
// D(A, C);
// };
// struct X {
// X();
// X(X&);
// };
// void e(A);
// void f(A);
// void f(B);
// void g(B);
// void h(C);
// void i(D);
// void x(const X);
//
// void test() {
// e( { 'a', 'b' }); // OK: f(A(std::initializer_list<int>)) user-defined conversion
// g( { 'a', 'b' }); // OK: g(B(int,double)) user-defined conversion
// g( { 1.0, 1.0 }); // error: narrowing
// f( { 'a', 'b' }); // error: ambiguous f(A) or f(B)
// h( { "foo" }); // OK: h(C(std::string("foo")))
// i( { { 1, 2 }, { "bar" } }); // OK: i(D(A(std::initializer_list<int>{1,2}),C(std::string("bar"))))
// X x1;
// x({x1}); // no matching constructor
// }
public void testListInitialization_302412c() throws Exception {
String code= getAboveComment();
BindingAssertionHelper bh= new BindingAssertionHelper(code, true);
bh.assertNonProblem("e( { 'a', 'b' })", 1);
bh.assertNonProblem("g( { 'a', 'b' })", 1);
bh.assertProblem("g( { 1.0, 1.0 })", 1);
bh.assertProblem("f( { 'a', 'b' })", 1);
bh.assertNonProblem("h( {", 1);
bh.assertNonProblem("i( { { 1, 2 }, {", 1);
bh.assertProblem("x({x1})", 1);
}
// namespace std {
// template<typename T> class initializer_list;
// }
// struct A {
// int m1;
// double m2;
// };
// void f(A);
// void test() {
// f( {'a', 'b'} ); // OK: f(A(int,double)) user-defined conversion
// f( {1.0} ); // narrowing not detected by cdt.
// }
public void testListInitialization_302412d() throws Exception {
String code= getAboveComment();
BindingAssertionHelper bh= new BindingAssertionHelper(code, true);
bh.assertNonProblem("f( {'a', 'b'} )", 1);
// not detected by CDT
// bh.assertProblem("f( {1.0} )", 1);
}
}

View file

@ -15,6 +15,7 @@ import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBasicType;
/**
* Arithmetic conversions as required to compute the type of unary or binary expressions.
@ -278,4 +279,43 @@ public abstract class ArithmeticConversion {
return IBasicType.IS_UNSIGNED | IBasicType.IS_LONG;
}
}
public static boolean fitsIntoType(ICPPBasicType basicTarget, long n) {
final Kind kind = basicTarget.getKind();
switch (kind) {
case eInt:
if (!basicTarget.isUnsigned()) {
if (basicTarget.isShort()) {
return Short.MIN_VALUE <= n && n <= Short.MAX_VALUE;
}
// Can't represent long longs with java longs.
if (basicTarget.isLong() || basicTarget.isLongLong()) {
return true;
}
return Integer.MIN_VALUE <= n && n <= Integer.MAX_VALUE;
}
if (n < 0)
return false;
if (basicTarget.isShort()) {
return n < (Short.MAX_VALUE + 1L)*2;
}
// Can't represent long longs with java longs.
if (basicTarget.isLong() || basicTarget.isLongLong()) {
return true;
}
return n < (Integer.MAX_VALUE + 1L)*2;
case eFloat:
float f= n;
return (long)f == n;
case eDouble:
double d= n;
return (long)d == n;
default:
return false;
}
}
}

View file

@ -2296,7 +2296,7 @@ public class CPPSemantics {
cost = new Cost(thisType, implicitType, Rank.IDENTITY);
} else {
cost = Conversions.checkImplicitConversionSequence(implicitType, thisType, sourceIsLValue, UDCMode.noUDC, true);
if (cost.getRank() == Rank.NO_MATCH) {
if (!cost.converts()) {
if (CPPTemplates.isDependentType(implicitType) || CPPTemplates.isDependentType(thisType)) {
IType s= getNestedType(thisType, TDEF|REF|CVTYPE);
IType t= getNestedType(implicitType, TDEF|REF|CVTYPE);
@ -2307,7 +2307,7 @@ public class CPPSemantics {
}
}
}
if (cost.getRank() == Rank.NO_MATCH)
if (!cost.converts())
return null;
result.setCost(k++, cost, sourceIsLValue);
@ -2338,8 +2338,11 @@ public class CPPSemantics {
if (CPPTemplates.isDependentType(paramType))
return CONTAINS_DEPENDENT_TYPES;
cost = Conversions.checkImplicitConversionSequence(paramType, argType, sourceIsLValue, udc, false);
if (data.fNoNarrowing && cost.isNarrowingConversion()) {
cost= Cost.NO_CONVERSION;
}
}
if (cost.getRank() == Rank.NO_MATCH)
if (!cost.converts())
return null;
result.setCost(k++, cost, sourceIsLValue);

View file

@ -140,7 +140,6 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownClassInstance;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownClassType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Conversions.UDCMode;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost.Rank;
/**
* Collection of static methods to perform template instantiation, member specialization and
@ -1966,7 +1965,7 @@ public class CPPTemplates {
paramType = new CPPPointerType(((IArrayType) paramType).getType());
}
Cost cost = Conversions.checkImplicitConversionSequence(paramType, arg, true, UDCMode.noUDC, false);
return cost != null && cost.getRank() != Rank.NO_MATCH;
return cost != null && cost.converts();
}
static boolean argsAreTrivial(ICPPTemplateParameter[] pars, ICPPTemplateArgument[] args) {

View file

@ -21,7 +21,9 @@ import java.util.BitSet;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBinding;
@ -33,6 +35,7 @@ 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.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerList;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBasicType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
@ -47,6 +50,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
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.Value;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName;
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;
@ -145,7 +149,7 @@ public class Conversions {
ok= !exprIsLValue;
}
if (!ok) {
return new Cost(exprType, cv1T1, Rank.NO_MATCH);
return Cost.NO_CONVERSION;
}
// If T1 and T2 are class types and ...
@ -204,7 +208,7 @@ public class Conversions {
return cost;
}
}
return new Cost(exprType, cv1T1, Rank.NO_MATCH);
return Cost.NO_CONVERSION;
}
// Non-reference binding
@ -274,14 +278,10 @@ public class Conversions {
// [13.3.3.1-6] Subsume cv-qualifications
IType uqSource= SemanticUtil.getNestedType(source, TDEF | ALLCVQ);
Cost cost= checkStandardConversionSequence(uqSource, sourceIsLValue, target, isImpliedObject);
if (cost.getRank() != Rank.NO_MATCH || udc == UDCMode.noUDC)
if (cost.converts() || udc == UDCMode.noUDC)
return cost;
Cost temp = checkUserDefinedConversionSequence(sourceIsLValue, source, target, udc == UDCMode.deferUDC);
if (temp != null) {
cost = temp;
}
return cost;
return checkUserDefinedConversionSequence(sourceIsLValue, source, target, udc == UDCMode.deferUDC);
}
/**
@ -296,7 +296,7 @@ public class Conversions {
for (int i = 0; i < exprTypes.length; i++) {
IType exprType = exprTypes[i];
Cost cost= checkImplicitConversionSequence(listType, exprType, isLValue.get(i), UDCMode.allowUDC, false);
if (cost.getRank() == Rank.NO_MATCH)
if (!cost.converts())
return cost;
if (cost.isNarrowingConversion()) {
cost.setRank(Rank.NO_MATCH);
@ -312,17 +312,17 @@ public class Conversions {
IType noCVTarget= getNestedType(target, CVTYPE | TDEF);
if (noCVTarget instanceof ICPPClassType) {
if (udc == UDCMode.noUDC)
return new Cost(arg, target, Rank.NO_MATCH);
return Cost.NO_CONVERSION;
ICPPClassType classTarget= (ICPPClassType) noCVTarget;
if (ClassTypeHelper.isAggregateClass(classTarget)) {
return new Cost(arg, target, Rank.USER_DEFINED_CONVERSION);
}
Cost cost= checkUserDefinedConversionSequence(false, arg, target, udc == UDCMode.deferUDC);
if (cost != null)
Cost cost= new Cost(arg, target, Rank.IDENTITY);
cost.setUserDefinedConversion(null);
return cost;
}
return checkUserDefinedConversionSequence(false, arg, target, udc == UDCMode.deferUDC);
}
return new Cost(arg, target, Rank.NO_MATCH);
return Cost.NO_CONVERSION;
}
private static IType getInitListType(IType target) throws DOMException {
@ -492,18 +492,13 @@ public class Conversions {
/**
* [13.3.3.1.2] User-defined conversions
* @param source
* @param target
* @return
* @throws DOMException
*/
static final Cost checkUserDefinedConversionSequence(boolean sourceIsLValue, IType source, IType target, boolean deferUDC) throws DOMException {
// mstodo don't return null
IType s= getNestedType(source, TDEF | CVTYPE | REF);
IType t= getNestedType(target, TDEF | CVTYPE | REF);
if (!(s instanceof ICPPClassType || t instanceof ICPPClassType)) {
return null;
return Cost.NO_CONVERSION;
}
if (deferUDC) {
@ -525,26 +520,25 @@ public class Conversions {
// 13.3.1.5 Initialization by conversion function
return initializationByConversion(source, (ICPPClassType) s, target);
}
return null;
return Cost.NO_CONVERSION;
}
private static Cost listInitializationOfClass(InitializerListType s, ICPPClassType t, boolean isDirect) throws DOMException {
private static Cost listInitializationOfClass(InitializerListType arg, ICPPClassType t, boolean isDirect) throws DOMException {
// If T has an initializer-list constructor
ICPPConstructor usedCtor= null;
Cost bestCost= null;
ICPPConstructor[] ctors= t.getConstructors();
boolean hasInitListConstructor= false;
final ICPPConstructor[] constructors = t.getConstructors();
ICPPConstructor[] ctors= constructors;
for (ICPPConstructor ctor : ctors) {
if (ctor.getRequiredArgumentCount() <= 1) {
IType[] parTypes= ctor.getType().getParameterTypes();
if (parTypes.length > 0) {
final IType target = parTypes[0];
if (getInitListType(target) != null) {
Cost cost= listInitializationSequence(s, target, UDCMode.noUDC);
if (cost.getRank() == Rank.NO_MATCH) {
if (bestCost == null) {
bestCost= cost;
}
} else {
hasInitListConstructor= true;
Cost cost= listInitializationSequence(arg, target, UDCMode.noUDC);
if (cost.converts()) {
int cmp= cost.compareTo(bestCost);
if (bestCost == null || cmp < 0) {
usedCtor= ctor;
@ -558,7 +552,10 @@ public class Conversions {
}
}
}
if (bestCost != null) {
if (hasInitListConstructor) {
if (bestCost == null)
return Cost.NO_CONVERSION;
if (!bestCost.isAmbiguousUDC() && !isDirect) {
if (usedCtor != null && usedCtor.isExplicit()) {
bestCost.setRank(Rank.NO_MATCH);
@ -566,10 +563,48 @@ public class Conversions {
}
return bestCost;
}
// mstodo expanding the list for selecting the ctor.
return new Cost(s, t, Rank.NO_MATCH);
// No initializer-list constructor
final ICPPASTInitializerList initializerList = arg.getInitializerList();
LookupData data= new LookupData();
IASTName name = new CPPASTName(t.getNameCharArray());
name.setParent(initializerList);
name.setPropertyInParent(CPPSemantics.STRING_LOOKUP_PROPERTY);
final IASTInitializerClause[] expandedArgs = initializerList.getClauses();
data.setFunctionArguments(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, t, Rank.IDENTITY);
c.setUserDefinedConversion((ICPPMethod) result);
} else if (result instanceof IProblemBinding
&& ((IProblemBinding) result).getID() == IProblemBinding.SEMANTIC_AMBIGUOUS_LOOKUP) {
c = new Cost(arg, t, Rank.USER_DEFINED_CONVERSION);
c.setAmbiguousUDC(true);
} else {
c= Cost.NO_CONVERSION;
}
return c;
}
/**
@ -646,8 +681,8 @@ public class Conversions {
}
}
}
if (cost1 == null || cost1.getCost(0).getRank() == Rank.NO_MATCH)
return null;
if (cost1 == null || !cost1.getCost(0).converts())
return Cost.NO_CONVERSION;
return cost2;
}
@ -667,7 +702,7 @@ public class Conversions {
boolean isLValue = uqReturnType instanceof ICPPReferenceType
&& !((ICPPReferenceType) uqReturnType).isRValueReference();
Cost c2= checkImplicitConversionSequence(target, uqReturnType, isLValue, UDCMode.noUDC, false);
if (c2.getRank() != Rank.NO_MATCH) {
if (c2.converts()) {
ICPPFunctionType ftype = op.getType();
IType implicitType= CPPSemantics.getImplicitType(op, ftype.isConst(), ftype.isVolatile());
final Cost udcCost = isReferenceCompatible(getNestedType(implicitType, TDEF | REF), source, true);
@ -686,8 +721,8 @@ public class Conversions {
}
}
}
if (cost1 == null || cost1.getCost(0).getRank() == Rank.NO_MATCH)
return null;
if (cost1 == null || !cost1.getCost(0).converts())
return Cost.NO_CONVERSION;
return cost2;
}

View file

@ -13,16 +13,23 @@
*******************************************************************************/
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.IType;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
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.
*/
final class Cost {
class Cost {
enum Rank {
IDENTITY, PROMOTION, CONVERSION, CONVERSION_PTR_BOOL,
USER_DEFINED_CONVERSION, ELLIPSIS_CONVERSION, NO_MATCH
@ -31,6 +38,41 @@ final class Cost {
RVALUE_REF_BINDS_RVALUE, LVALUE_REF, OTHER
}
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(boolean 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;
}
};
IType source;
IType target;
@ -52,10 +94,14 @@ final class Cost {
fReferenceBinding= ReferenceBinding.OTHER;
}
public Rank getRank() {
public final Rank getRank() {
return fRank;
}
public final boolean converts() {
return fRank != Rank.NO_MATCH;
}
public void setRank(Rank rank) {
fRank= rank;
}
@ -99,10 +145,9 @@ final class Cost {
*/
public void setUserDefinedConversion(ICPPMethod conv) {
fUserDefinedConversion= conv;
if (conv != null) {
fSecondStandardConversionRank= fRank;
fRank= Rank.USER_DEFINED_CONVERSION;
}
fSecondStandardConversionRank= fRank;
fRank= Rank.USER_DEFINED_CONVERSION;
fCouldNarrow= false;
}
/**
@ -189,7 +234,26 @@ final class Cost {
}
public boolean isNarrowingConversion() {
return fCouldNarrow;
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() {

View file

@ -17,7 +17,6 @@ import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost.Rank;
/**
* Cost for the entire function call
@ -66,6 +65,8 @@ class FunctionCost {
public boolean hasDeferredUDC() {
for (Cost cost : fCosts) {
if (!cost.converts())
return false;
if (cost.isDeferredUDC())
return true;
}
@ -77,10 +78,10 @@ class FunctionCost {
Cost cost = fCosts[i];
if (cost.isDeferredUDC()) {
Cost udcCost= Conversions.checkUserDefinedConversionSequence(fSourceIsLValue.get(i), cost.source, cost.target, false);
if (udcCost == null || udcCost.getRank() == Rank.NO_MATCH) {
fCosts[i]= udcCost;
if (!udcCost.converts()) {
return false;
}
fCosts[i]= udcCost;
}
}
return true;
@ -102,7 +103,7 @@ class FunctionCost {
int idxOther= other.getLength() - 1;
for (; idx >= 0 && idxOther >= 0; idx--, idxOther--) {
Cost cost= getCost(idx);
if (cost.getRank() == Rank.NO_MATCH) {
if (!cost.converts()) {
haveWorse = true;
haveBetter = false;
break;
@ -155,7 +156,7 @@ class FunctionCost {
int idxOther= other.getLength() - 1;
for (; idx >= 0 && idxOther >= 0; idx--, idxOther--) {
Cost cost= getCost(idx);
if (cost.getRank() == Rank.NO_MATCH)
if (!cost.converts())
return true;
Cost otherCost= other.getCost(idxOther);

View file

@ -101,6 +101,8 @@ public class LookupData {
/** When computing the cost of a method call, treat the first argument as the implied method argument. */
public boolean firstArgIsImpliedMethodArg = false;
public boolean ignoreMembers = false;
/** In list-initialization **/
public boolean fNoNarrowing= false;
private ICPPASTParameterDeclaration[] functionParameters;
private IASTInitializerClause[] functionArgs;