From 0757b45da5db47a65f59911c42eb9dcccfbdf972 Mon Sep 17 00:00:00 2001 From: Hannes Vogt Date: Wed, 5 Dec 2018 20:17:28 +0100 Subject: [PATCH] Bug 543038 - Implement brace elision for aggregate initialization Change-Id: I72e92c8c196bf201c8edfde64598a7318d2fdfeb Signed-off-by: Hannes Vogt --- .../core/parser/tests/ast2/AST2CPPTests.java | 139 +++++++++++++ .../core/dom/parser/ProblemBinding.java | 10 +- .../semantics/AggregateInitialization.java | 182 ++++++++++++++++++ .../dom/parser/cpp/semantics/Conversions.java | 120 +----------- .../dom/parser/cpp/semantics/EvalTypeId.java | 10 +- 5 files changed, 347 insertions(+), 114 deletions(-) create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AggregateInitialization.java 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 b32453e95e7..93aeab5dc0a 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 @@ -12843,11 +12843,150 @@ public class AST2CPPTests extends AST2CPPTestBase { // // int main() { // type(1, 2); + // type{1, 2}; // type(other_type()); // } public void testCtorWithWrongArguments_543913() throws Exception { BindingAssertionHelper bh = getAssertionHelper(); bh.assertImplicitName("type(1, 2)", 4, IProblemBinding.class); + bh.assertImplicitName("type{1, 2}", 4, IProblemBinding.class); bh.assertImplicitName("type(other_type())", 4, IProblemBinding.class); } + + // struct array{ + // int data[1]; + // }; + // + // void foo(array) {} + // + // int main() { + // array{{1}}; + // array{1}; + // array a = {1}; + // foo({1}); + // foo({{1}}); + // } + public void testBraceElisionForAggregateInit0_SimpleValid_543038() throws Exception { + parseAndCheckBindings(); + } + + // struct array{ + // int data[1]; + // }; + // + // void foo(array) {} + // + // int main() { + // array{{1,2}}; + // array{1,2}; + // array a0 = {1,2}; + // foo({1,2}); + // foo({{1,2}}); + // } + public void testBraceElisionForAggregateInit1_SimpleTooManyInitializers_543038() throws Exception { + BindingAssertionHelper bh = getAssertionHelper(); + // bh.assertProblem("array{{1,2}}", 5); // TODO not implemented + // bh.assertProblem("array{1,2}", 5); // TODO not implemented + bh.assertImplicitName("a0", 2, IProblemBinding.class); + bh.assertProblem("foo({1,2})", 3); + bh.assertProblem("foo({{1,2}})", 3); + } + + // struct level0{ + // int a; + // int b; + // }; + // + // struct level1{ + // public: + // level1(level0 a): a(a){} + // private: + // level0 a; + // }; + // + // struct level2{ + // level1 data; + // }; + // + // void foo(level2) {} + // + // int main() { + // level1{{1,2}}; // ok + // foo({level1{{1,2}}}); // ok + // level1{1,2}; // ERROR: calling level1 constructor, not aggregate init of level0 + // foo({{{1,2,3}}}); // ERROR: not aggregate init + // } + public void testBraceElisionForAggregateInit2_WithNonAggregate_543038() throws Exception { + BindingAssertionHelper bh = getAssertionHelper(); + + bh.assertNonProblem("foo({level1{{1,2}}})", 3); + + ICPPConstructor ctor = bh.assertNonProblem("level1(level0 a)", "level1"); + ICPPASTSimpleTypeConstructorExpression typeConstructorExpr = bh.assertNode("level1{{1,2}};", "level1{{1,2}}"); + IASTImplicitName[] implicitNames = ((IASTImplicitNameOwner) typeConstructorExpr).getImplicitNames(); + assertEquals(ctor, implicitNames[0].resolveBinding()); + + bh.assertImplicitName("level1{1,2};", 6, IProblemBinding.class); + bh.assertProblem("foo({{{1,2,3}}}", 3); + } + + // struct array2D{ + // int data[2][3]; + // }; + // + // void foo(array2D) {} + // + // int main() { + // foo({{{1,2,3},{1,2,3}}}); // no elision + // foo({{1,2,3,1,2,3}}); // eliding one level + // foo({{1,2}}); // eliding one level, but with only 2 elements which seems to initialize the outer level + // foo({1,2,3,1,2,3}); // eliding all levels + // foo({{1,2,3},{1,2,3}}); // ERROR eliding outer-most is not allowed + // } + public void testBraceElisionForAggregateInit3_543038() throws Exception { + BindingAssertionHelper bh = getAssertionHelper(); + bh.assertNonProblem("foo({{{1,2,3},{1,2,3}}});", 3); + bh.assertNonProblem("foo({{1,2,3,1,2,3}});", 3); + bh.assertNonProblem("foo({{1,2}});", 3); + bh.assertNonProblem("foo({1,2,3,1,2,3});", 3); + bh.assertProblem("foo({{1,2,3},{1,2,3}});", 3); + } + + // struct type{ + // type(int){}; + // }; + // + // struct array{ + // type data[2]; + // }; + // + // void foo(array){} + // + // int main() { + // foo({type{1},type{2}}); + // foo({type{1}}); // ERROR: type is not default constructible + // } + public void testBraceElisionForAggregateInit4_nonDefaultConstructible_543038() throws Exception { + BindingAssertionHelper bh = getAssertionHelper(); + bh.assertNonProblem("foo({type{1},type{2}});", 3); + bh.assertProblem("foo({type{1}});", 3); + } + + // struct array2D{ + // int data[2][3]; + // }; + // + // void foo(array2D) {} + // + // int main() { + // foo({{{1,2,3},1,2,3}}); // ok: data[0] is initialized without elision, data[1] with elision + // foo({{1,2,3,{1,2,3}}}); // ok: data[1] is initialized without elision, data[0] with elision + // foo({{1,2,{1,2,3}}}); // ERROR: trying to initialize data[0][2] with {1,2,3} + // } + public void testBraceElisionForAggregateInit5_partlyEliding_543038() throws Exception { + BindingAssertionHelper bh = getAssertionHelper(); + bh.assertNonProblem("foo({{{1,2,3},1,2,3}});", 3); + bh.assertNonProblem("foo({{1,2,3,{1,2,3}}});", 3); + bh.assertProblem("foo({{1,2,{1,2,3}}});", 3); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemBinding.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemBinding.java index 00ccb61ae42..6c2498d59e1 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemBinding.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemBinding.java @@ -150,12 +150,18 @@ public class ProblemBinding extends PlatformObject implements IProblemBinding, I @Override public String getName() { - return node instanceof IASTName ? new String(((IASTName) node).getSimpleID()) : CPPSemantics.EMPTY_NAME; + if (node instanceof IASTName) + return new String(((IASTName) node).getSimpleID()); + else + return arg != null ? new String(arg) : CPPSemantics.EMPTY_NAME; } @Override public char[] getNameCharArray() { - return node instanceof IASTName ? ((IASTName) node).getSimpleID() : CharArrayUtils.EMPTY; + if (node instanceof IASTName) + return ((IASTName) node).getSimpleID(); + else + return arg != null ? arg : CharArrayUtils.EMPTY; } @Override diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AggregateInitialization.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AggregateInitialization.java new file mode 100644 index 00000000000..68401ab2834 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AggregateInitialization.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics; + +import org.eclipse.cdt.core.dom.ast.DOMException; +import org.eclipse.cdt.core.dom.ast.IArrayType; +import org.eclipse.cdt.core.dom.ast.IType; +import org.eclipse.cdt.core.dom.ast.IValue; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPField; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType; +import org.eclipse.cdt.core.parser.util.ArrayUtil; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Conversions.Context; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Conversions.UDCMode; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost.Rank; + +class AggregateInitialization { + private ICPPEvaluation[] fInitializers; + private int fIndex = 0; + + /** + * Checks whether 'target' can be initialized from 'list' according to the rules for + * aggregate initialization ([dcl.init.aggr]). + */ + public static Cost check(IType target, EvalInitList list) throws DOMException { + return new AggregateInitialization().checkImpl(target, list); + } + + private AggregateInitialization() { + } + + private Cost checkImpl(IType target, EvalInitList list) throws DOMException { + fInitializers = list.getClauses(); + fIndex = 0; + + Cost worstCost = new Cost(list.getType(), target, Rank.IDENTITY); + + Cost cost = checkInitializationOfElements(target, worstCost); + if (!cost.converts()) + return cost; + + if (fIndex < fInitializers.length) + // p7: An initializer-list is ill-formed if the number of initializer-clauses exceeds + // the number of members to initialize. + return Cost.NO_CONVERSION; + else + return worstCost; + } + + /** + * If no braces are elided, check initialization of element by taking the next clause from the EvalInitList, + * else recurses into the subaggregate. + */ + private Cost checkElement(IType type, IValue initialValue, Cost worstCost) throws DOMException { + if (fIndex >= fInitializers.length) + // TODO for arrays we could short-circuit default init instead of trying to init each element + return checkInitializationFromDefaultMemberInitializer(type, initialValue, worstCost); + worstCost = new Cost(fInitializers[fIndex].getType(), type, Rank.IDENTITY); + + if (fInitializers[fIndex].isInitializerList() || !isAggregate(type)) { // no braces are elided + // p3: The elements of the initializer list are taken as initializers for the elements + // of the aggregate, in order. + ICPPEvaluation initializer = fInitializers[fIndex]; + fIndex++; + Cost cost = Conversions.checkImplicitConversionSequence(type, initializer.getType(), + initializer.getValueCategory(), UDCMode.ALLOWED, Context.ORDINARY); + if (!cost.converts()) { + return cost; + } + // 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()) { + return Cost.NO_CONVERSION; + } + if (cost.compareTo(worstCost) > 0) { + worstCost = cost; + } + } else { // braces are elided: need to check on subaggregates + Cost cost = checkInitializationOfElements(type, worstCost); + if (!cost.converts()) + return cost; + if (cost.compareTo(worstCost) > 0) { + worstCost = cost; + } + } + return worstCost; + } + + /** + * checkElement() for each element of an array or each field of a class aggregate. + */ + private Cost checkInitializationOfElements(IType type, Cost worstCost) throws DOMException { + if (type instanceof ICPPClassType && TypeTraits.isAggregateClass((ICPPClassType) type)) { + ICPPField[] fields = getFieldsForAggregateInitialization((ICPPClassType) type); + for (ICPPField field : fields) { + Cost cost = checkElement(field.getType(), field.getInitialValue(), worstCost); + if (!cost.converts()) + return cost; + if (cost.compareTo(worstCost) > 0) { + worstCost = cost; + } + } + } else if (type instanceof IArrayType) { + IArrayType arrayType = (IArrayType) type; + Number arraySize = arrayType.getSize().numberValue(); + if (arraySize != null) + for (long i = 0; i < arraySize.longValue(); i++) { + Cost cost = checkElement(arrayType.getType(), null, worstCost); + if (!cost.converts()) + return cost; + if (cost.compareTo(worstCost) > 0) { + worstCost = cost; + } + } + } + return worstCost; + } + + /** + * 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. + */ + private Cost checkInitializationFromDefaultMemberInitializer(IType type, IValue initialValue, Cost worstCost) + throws DOMException { + if (initialValue != null) { + return worstCost; // 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(type, SemanticUtil.TDEF); + if (fieldType instanceof ICPPReferenceType) { + return Cost.NO_CONVERSION; + } + + // Empty initializer list + EvalInitList emptyInit = new EvalInitList(ICPPEvaluation.EMPTY_ARRAY, CPPSemantics.getCurrentLookupPoint()); + Cost cost = Conversions.listInitializationSequence(emptyInit, fieldType, UDCMode.ALLOWED, false); + return cost; + } + + private static boolean isAggregate(IType type) { + return (type instanceof ICPPClassType && TypeTraits.isAggregateClass((ICPPClassType) type)) + || type instanceof IArrayType; + } + + /** + * 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). + */ + private static ICPPField[] getFieldsForAggregateInitialization(ICPPClassType targetClass) { + ICPPField[] fields = targetClass.getDeclaredFields(); + 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); + } + +} \ No newline at end of file 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 189d802e8e2..61627f970d6 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 @@ -49,7 +49,6 @@ 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; @@ -58,7 +57,6 @@ 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; @@ -334,97 +332,6 @@ public class Conversions { return checkStandardConversionSequence(uqSource, target); } - /** - * 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) { - ICPPField[] fields = targetClass.getDeclaredFields(); - 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) throws DOMException { - ICPPField[] fields = getFieldsForAggregateInitialization(targetClass); - 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(), - initializer.getValueCategory(), UDCMode.ALLOWED, Context.ORDINARY); - 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()) { - 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, CPPSemantics.getCurrentLookupPoint()); - Cost cost = listInitializationSequence(emptyInit, fieldType, UDCMode.ALLOWED, false); - if (!cost.converts()) { - return false; - } - } - - // TODO: Implement brace elision rules. - - return true; - } - /** * 13.3.3.1.5 List-initialization sequence [over.ics.list] */ @@ -438,22 +345,9 @@ public class Conversions { static Cost listInitializationSequenceHelper(EvalInitList arg, IType target, UDCMode udc, boolean isDirect) throws DOMException { IType listType = getInitListType(target); - if (listType == null && target instanceof IArrayType) { - Number arraySize = ((IArrayType) target).getSize().numberValue(); - if (arraySize != null) { - IType elementType = ((IArrayType) target).getType(); - // TODO(nathanridge): If there are fewer initializer clauses than the array size, - // then the element type is required to be default-constructible. - if (arg.getClauses().length <= arraySize.longValue()) { - listType = elementType; - } - } - } - if (listType != null) { - ICPPEvaluation[] clauses = arg.getClauses(); Cost worstCost = new Cost(arg.getType(), target, Rank.IDENTITY); - for (ICPPEvaluation clause : clauses) { + for (ICPPEvaluation clause : arg.getClauses()) { Cost cost = checkImplicitConversionSequence(listType, clause.getType(), clause.getValueCategory(), UDCMode.ALLOWED, Context.ORDINARY); if (!cost.converts()) @@ -467,6 +361,8 @@ public class Conversions { } } return worstCost; + } else if (target instanceof IArrayType) { + return AggregateInitialization.check(target, arg); } IType noCVTarget = getNestedType(target, CVTYPE | TDEF); @@ -475,10 +371,12 @@ public class Conversions { return Cost.NO_CONVERSION; ICPPClassType classTarget = (ICPPClassType) noCVTarget; - if (TypeTraits.isAggregateClass(classTarget) && checkAggregateInitialization(arg, classTarget)) { - Cost cost = new Cost(arg.getType(), target, Rank.IDENTITY); - cost.setUserDefinedConversion(null); - return cost; + if (TypeTraits.isAggregateClass(classTarget)) { + Cost cost = AggregateInitialization.check(classTarget, arg); + if (cost.converts()) { + cost.setUserDefinedConversion(null); + return cost; + } } return listInitializationOfClass(arg, classTarget, isDirect, udc == UDCMode.DEFER); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalTypeId.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalTypeId.java index 7cf484afb54..63837ab46f9 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalTypeId.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalTypeId.java @@ -272,7 +272,7 @@ public class EvalTypeId extends CPPDependentEvaluation { ICPPClassType classType = (ICPPClassType) simplifiedType; ICPPEvaluation[] arguments = fArguments; ICPPConstructor[] constructors = classType.getConstructors(); - if (arguments.length == 1 && arguments[0] instanceof EvalInitList) { + if (arguments.length == 1 && arguments[0] instanceof EvalInitList && !fUsesBracedInitList) { // List-initialization of a class (dcl.init.list-3). if (TypeTraits.isAggregateClass(classType)) { // Pretend that aggregate initialization is calling the default constructor. @@ -299,10 +299,18 @@ public class EvalTypeId extends CPPDependentEvaluation { if (binding instanceof ICPPFunction) { return (ICPPFunction) binding; } + // TODO check aggregate initialization + if (fUsesBracedInitList && allConstructorsAreCompilerGenerated(constructors)) { + return AGGREGATE_INITIALIZATION; + } + if (binding instanceof IProblemBinding && !(binding instanceof ICPPFunction)) + return new CPPFunction.CPPFunctionProblem(null, ((IProblemBinding) binding).getID(), + classType.getNameCharArray()); } catch (DOMException e) { CCorePlugin.log(e); } + // TODO check aggregate initialization if (fUsesBracedInitList && allConstructorsAreCompilerGenerated(constructors)) { return AGGREGATE_INITIALIZATION; }