From c1b801b036f93a6b5a484b53613e8bac765f2578 Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Fri, 20 Dec 2013 03:12:13 -0500 Subject: [PATCH] Bug 332829 - Handle ambiguity between a type-id and an expression other than an id-expression in a template argument Change-Id: I1ec6157b09526a1f98850361f903fbea8b8c8a89 Signed-off-by: Nathan Ridge Reviewed-on: https://git.eclipse.org/r/20140 Reviewed-by: Sergey Prigogin IP-Clean: Sergey Prigogin Tested-by: Sergey Prigogin --- .../parser/tests/ast2/AST2TemplateTests.java | 14 +++ .../cpp/ICPPASTAmbiguousTemplateArgument.java | 14 ++- .../cpp/CPPASTAmbiguousTemplateArgument.java | 27 +++--- .../dom/parser/cpp/GNUCPPSourceParser.java | 94 +++++++++++++------ 4 files changed, 100 insertions(+), 49 deletions(-) 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 de894db0350..c5679dcd0dd 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 @@ -7265,6 +7265,20 @@ public class AST2TemplateTests extends AST2TestBase { assertEquals("bool", ASTTypeUtil.getType(td.getType())); ah.assertProblem("B::type", "type"); } + + // constexpr int f() { return 1; } + // + // template + // struct A { + // static void g() {} + // }; + // + // void bar() { + // A::g(); + // } + public void testConstexprFunctionCallInTemplateArgument_332829() throws Exception { + parseAndCheckBindings(); + } // template // struct is_convertible { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTAmbiguousTemplateArgument.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTAmbiguousTemplateArgument.java index a11c134330e..6bad894e866 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTAmbiguousTemplateArgument.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTAmbiguousTemplateArgument.java @@ -26,11 +26,11 @@ public interface ICPPASTAmbiguousTemplateArgument extends IASTNode { /** * Add an partial parse tree that could be a suitable subtree representing * the template argument - * @param idExpression a non-null id-expression or a pack expansion of an id-expression - * @since 5.2 + * @param expression a non-null expression + * @since 5.6 */ - public void addIdExpression(IASTExpression idExpression); - + public void addExpression(IASTExpression expression); + /** * Add an partial parse tree that could be a suitable subtree representing * the template argument @@ -38,6 +38,12 @@ public interface ICPPASTAmbiguousTemplateArgument extends IASTNode { */ public void addTypeId(IASTTypeId typeId); + /** + * @deprecated Replaced by {@link #addExpression(IASTExpression)}. + */ + @Deprecated + public void addIdExpression(IASTExpression idExpression); + /** * @deprecated Replaced by {@link #addIdExpression(IASTExpression)}. */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguousTemplateArgument.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguousTemplateArgument.java index e6b02b0356c..3314d7193b9 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguousTemplateArgument.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguousTemplateArgument.java @@ -31,7 +31,7 @@ import org.eclipse.cdt.internal.core.dom.parser.ASTAmbiguousNode; import org.eclipse.core.runtime.Assert; /** - * Ambiguity node for deciding between type-id and id-expression in a template argument. + * Ambiguity node for deciding between type-id and expression in a template argument. */ public class CPPASTAmbiguousTemplateArgument extends ASTAmbiguousNode implements ICPPASTAmbiguousTemplateArgument { private List fNodes; @@ -43,15 +43,8 @@ public class CPPASTAmbiguousTemplateArgument extends ASTAmbiguousNode implements public CPPASTAmbiguousTemplateArgument(IASTNode... nodes) { fNodes= new ArrayList(2); for (IASTNode node : nodes) { - if (node instanceof IASTTypeId || node instanceof IASTIdExpression) { + if (node instanceof IASTTypeId || node instanceof IASTExpression) { fNodes.add(node); - } else if (node instanceof ICPPASTPackExpansionExpression) { - final IASTExpression pattern = ((ICPPASTPackExpansionExpression) node).getPattern(); - if (pattern instanceof IASTIdExpression) { - fNodes.add(node); - } else { - Assert.isLegal(false, pattern == null ? "null" : pattern.getClass().getName()); //$NON-NLS-1$ - } } else { Assert.isLegal(false, node == null ? "null" : node.getClass().getName()); //$NON-NLS-1$ } @@ -60,7 +53,9 @@ public class CPPASTAmbiguousTemplateArgument extends ASTAmbiguousNode implements @Override protected void beforeAlternative(IASTNode node) { - // The name may be shared between the alternatives make sure it's parent is set correctly + // If the expression is an id-expression, the name may be shared + // between the alternatives (see bug 316704), so make sure its parent + // is set correctly. if (node instanceof IASTTypeId) { IASTDeclSpecifier declSpec = ((IASTTypeId) node).getDeclSpecifier(); if (declSpec instanceof IASTNamedTypeSpecifier) { @@ -122,16 +117,20 @@ public class CPPASTAmbiguousTemplateArgument extends ASTAmbiguousNode implements addNode(typeId); } + @Override + public void addExpression(IASTExpression expression) { + assertNotFrozen(); + addNode(expression); + } + @Override public void addIdExpression(IASTIdExpression idExpression) { - assertNotFrozen(); - addNode(idExpression); + addExpression(idExpression); } @Override public void addIdExpression(IASTExpression idExpression) { - assertNotFrozen(); - addNode(idExpression); + addExpression(idExpression); } private void addNode(IASTNode node) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java index 0474384483b..b0176f41edd 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java @@ -658,46 +658,78 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { if (typeId != null && (lt1 == IToken.tCOMMA || lt1 == IToken.tGT || lt1 == IToken.tGT_in_SHIFTR || lt1 == IToken.tEOC || lt1 == IToken.tELLIPSIS)) { - // This is potentially a type-id, now check ambiguity with id-expression - IASTDeclSpecifier declspec= typeId.getDeclSpecifier(); - if (declspec instanceof IASTNamedTypeSpecifier) { - final IASTNamedTypeSpecifier namedDeclspec = (IASTNamedTypeSpecifier) declspec; - IASTName name= namedDeclspec.getName(); - if (name.contains(typeId)) { - IToken typeIdEnd= mark(); - IASTIdExpression idExpr= setRange(nodeFactory.newIdExpression(name), name); - try { - IASTExpression expression = expression(ExprKind.eAssignment, BinaryExprCtx.eInTemplateID, idExpr, strat); - boolean isAmbiguous= (expression == idExpr); - if (LT(1) == IToken.tELLIPSIS) { - IToken ellipsis= consume(); - if (isAmbiguous) { - addPackExpansion(typeId, ellipsis); - } - expression= addPackExpansion(expression, ellipsis); - } - if (isAmbiguous) { - ICPPASTAmbiguousTemplateArgument ambiguity= createAmbiguousTemplateArgument(); - ambiguity.addTypeId(typeId); - ambiguity.addIdExpression(expression); - return ambiguity; - } - return expression; - } catch (BacktrackException e) { - // Use the typeId + // This is potentially a type-id, now check ambiguity with expression. + IToken typeIdEnd= mark(); + IASTNamedTypeSpecifier namedTypeSpec = null; + IASTName name = null; + try { + // If the type-id consists of a name, that name could be or contain + // a template-id, with template arguments of its own, which can + // themselves be ambiguous. If we parse the name anew as an + // id-expression, our complexity becomes exponential in the nesting + // depth of template-ids (bug 316704). To avoid this, we do not + // re-parse the name, but instead synthesize an id-expression from + // it, and then continue parsing an expression from the id-expression + // onwards (as the id-expression could be the beginning of a larger + // expression). + IASTIdExpression idExpression = null; + IASTDeclSpecifier declSpec = typeId.getDeclSpecifier(); + if (declSpec instanceof IASTNamedTypeSpecifier) { + namedTypeSpec = (IASTNamedTypeSpecifier) declSpec; + name = namedTypeSpec.getName(); + if (name.contains(typeId)) { + idExpression = setRange(nodeFactory.newIdExpression(name), name); } - backup(typeIdEnd); - namedDeclspec.setName(name); } + + // Parse an expression, starting with the id-expression synthesized + // above if there is one, otherwise starting from the beginning of + // the argument. + if (idExpression == null) + backup(argStart); + IASTExpression expression = expression(ExprKind.eAssignment, BinaryExprCtx.eInTemplateID, idExpression, strat); + + // At this point we have a valid type-id and a valid expression. + // We prefer the longer one. + if (!typeId.contains(expression)) { + // The expression is longer. + if (LT(1) == IToken.tELLIPSIS) { + expression = addPackExpansion(expression, consume()); + } + return expression; + } else if (expression.contains(typeId)) { + // The two are of the same length - ambiguous. + if (LT(1) == IToken.tELLIPSIS) { + IToken ellipsis = consume(); + addPackExpansion(typeId, ellipsis); + expression = addPackExpansion(expression, ellipsis); + } + ICPPASTAmbiguousTemplateArgument ambiguity = createAmbiguousTemplateArgument(); + ambiguity.addTypeId(typeId); + ambiguity.addExpression(expression); + return ambiguity; + } + // The type-id is longer, use it. + } catch (BacktrackException e) { + // Failed to parse an expression, use the type id. } - // There is no ambiguity, use the type-id + + // Clean up after our failed attempt to parse an expression. + backup(typeIdEnd); + if (name != null && namedTypeSpec != null) { + // When we synthesized the id-expression, it took ownership + // of the name. Give ownership back to the type-id. + namedTypeSpec.setName(name); + } + + // Use the type-id. if (LT(1) == IToken.tELLIPSIS) { addPackExpansion(typeId, consume()); } return typeId; } - // Not a type-id, parse as expression + // Not a type-id, parse as expression. backup(argStart); IASTExpression expr= expression(ExprKind.eAssignment, BinaryExprCtx.eInTemplateID, null, strat); if (LT(1) == IToken.tELLIPSIS) {