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 caa0900b04a..22c28a98371 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 @@ -8926,6 +8926,27 @@ public class AST2CPPTests extends AST2TestBase { assertTrue(binding instanceof ICPPConstructor); assertEquals(2, ((ICPPConstructor) binding).getType().getParameterTypes().length); } + + // struct S { + // int a; + // float b; + // char c; + // }; + // + // struct T { + // char x; + // int y; + // }; + // + // void foo(S); + // void foo(T); + // + // int main() { + // foo({3, 4.0, 'c'}); // 'foo' is ambiguous + // } + public void testAggregateInitialization_487555() throws Exception { + parseAndCheckBindings(); + } // namespace std { // template class initializer_list; 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 efe3fb8ca0d..c6f2d956779 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 @@ -47,6 +47,7 @@ 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; @@ -55,6 +56,7 @@ 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; @@ -327,6 +329,98 @@ public class Conversions { 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] */ @@ -375,7 +469,8 @@ public class Conversions { return Cost.NO_CONVERSION; ICPPClassType classTarget= (ICPPClassType) noCVTarget; - if (TypeTraits.isAggregateClass(classTarget, point)) { + if (TypeTraits.isAggregateClass(classTarget, point) && + checkAggregateInitialization(arg, classTarget, point)) { Cost cost= new Cost(arg.getType(point), target, Rank.IDENTITY); cost.setUserDefinedConversion(null); return cost;