From 477c241751ac9dc0ed95d93268003e89661f1ff0 Mon Sep 17 00:00:00 2001 From: Markus Schorn Date: Fri, 12 Mar 2010 13:52:28 +0000 Subject: [PATCH] Bug 302412: Template argument deduction for initializer lists. --- .../core/parser/tests/ast2/AST2CPPTests.java | 30 +++++++++++++++++-- .../parser/tests/ast2/AST2TemplateTests.java | 21 +++++++++++++ .../dom/parser/cpp/semantics/Conversions.java | 23 +++++++++++--- .../semantics/TemplateArgumentDeduction.java | 18 +++++++++++ 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java index 32abcdfdc85..3bd7f859a0d 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java @@ -8118,12 +8118,14 @@ public class AST2CPPTests extends AST2BaseTest { // namespace std { // template class initializer_list; // } - // void f(std::initializer_list); - // //void ff(std::initializer_list); + // struct str { + // str(const char*); + // }; // struct A { // A(std::initializer_list); // #1 - // A(std::initializer_list); // #3 + // A(std::initializer_list); // #3 // }; + // void f(std::initializer_list); // void g(A); // void test() { // f( {1,2,3} ); // OK: f(initializer_list) identity conversion @@ -8213,5 +8215,27 @@ public class AST2CPPTests extends AST2BaseTest { // not detected by CDT // bh.assertProblem("f( {1.0} )", 1); } + + // namespace std { + // template class initializer_list; + // } + // void g(const double &); + // void h(int); + // void test() { + // g({1}); // same conversion as int to double + // + // h( {'a'} ); // OK: same conversion as char to int + // h( {1.0} ); // error: narrowing + // h( { } ); // OK: identity conversion + // } + public void testListInitialization_302412e() throws Exception { + String code= getAboveComment(); + BindingAssertionHelper bh= new BindingAssertionHelper(code, true); + // not detected by CDT + // bh.assertProblem("g({1})", 1); + bh.assertNonProblem("h( {'a'} )", 1); + bh.assertProblem("h( {1.0} )", 1); + bh.assertNonProblem("h( { } )", 1); + } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java index a4997194576..6851f6b0f6f 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java @@ -4876,4 +4876,25 @@ public class AST2TemplateTests extends AST2BaseTest { final String code= getAboveComment(); parseAndCheckBindings(code, ParserLanguage.CPP); } + + // namespace std { + // template class initializer_list; + // } + // template void f(std::initializer_list); + // template void g(T); + // void test() { + // f({1,2,3}); // T deduced to int + // f({1,"asdf"}); // error: T deduced to both int and const char* + // g({1,2,3}); // error: no argument deduced for T + // } + public void testTypeDeductForInitLists_302412() throws Exception { + final String code= getAboveComment(); + BindingAssertionHelper bh= new BindingAssertionHelper(code, true); + + ICPPTemplateInstance inst; + inst= bh.assertNonProblem("f({1,2,3})", 1); + assertEquals("", ASTTypeUtil.getArgumentListString(inst.getTemplateArguments(), true)); + bh.assertProblem("f({1,\"asdf\"})", 1); + bh.assertProblem("g({1,2,3})", 1); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/Conversions.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/Conversions.java index 3bec003c077..8c607c9d1b5 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/Conversions.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/Conversions.java @@ -37,7 +37,6 @@ 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; import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionType; @@ -202,7 +201,7 @@ public class Conversions { if (!isImpliedObjectType) { if (isReferenceRelated(T1, T2) < 0 || compareQualifications(cv1T1, cv2T2) >= 0) { Cost cost= nonReferenceConversion(exprIsLValue, cv2T2, T1, udc, false); - if (!isImplicitWithoutRefQualifier) { + if (!isImplicitWithoutRefQualifier && cost.converts()) { cost.setReferenceBinding(isLValueRef ? ReferenceBinding.LVALUE_REF : ReferenceBinding.RVALUE_REF_BINDS_RVALUE); } return cost; @@ -322,11 +321,27 @@ public class Conversions { } return checkUserDefinedConversionSequence(false, arg, target, udc == UDCMode.deferUDC); } + + IASTInitializerClause[] args = arg.getInitializerList().getClauses(); + if (args.length == 1) { + final IASTInitializerClause firstArg = args[0]; + if (firstArg instanceof IASTExpression) { + IASTExpression expr= (IASTExpression) firstArg; + Cost cost= checkImplicitConversionSequence(target, expr.getExpressionType(), expr.isLValue(), udc, false); + if (cost.isNarrowingConversion()) { + return Cost.NO_CONVERSION; + } + return cost; + } + } else if (args.length == 0) { + return new Cost(arg, target, Rank.IDENTITY); + } + return Cost.NO_CONVERSION; } - private static IType getInitListType(IType target) throws DOMException { - if (target instanceof ICPPClassSpecialization && target instanceof ICPPTemplateInstance) { + static IType getInitListType(IType target) throws DOMException { + if (target instanceof ICPPClassType && target instanceof ICPPTemplateInstance) { ICPPTemplateInstance inst = (ICPPTemplateInstance) target; if (CharArrayUtils.equals(INITIALIZER_LIST_NAME, inst.getNameCharArray())) { IBinding owner = inst.getOwner(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/TemplateArgumentDeduction.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/TemplateArgumentDeduction.java index 6a1d2bafdd4..68fd65555da 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/TemplateArgumentDeduction.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/TemplateArgumentDeduction.java @@ -190,6 +190,24 @@ public class TemplateArgumentDeduction { boolean isReferenceTypeParameter= false; IType arg = fnArgs[j]; par= SemanticUtil.getNestedType(par, SemanticUtil.TDEF); // adjustParameterType preserves typedefs + + // C++0x: 14.9.2.1-1 + if (arg instanceof InitializerListType) { + assert !checkExactMatch; + par= SemanticUtil.getNestedType(par, TDEF | REF | CVTYPE); + + // Check if this is a deduced context + IType inner= Conversions.getInitListType(par); + if (inner != null) { + IType[] types = ((InitializerListType) arg).getExpressionTypes(); + for (IType iType : types) { + if (!deduct.fromType(inner, iType)) + return false; + } + } + continue; + } + // 14.8.2.1-2 if (par instanceof ICPPReferenceType) { // If P is an rvalue reference to a cv-unqualified template parameter and the argument is an