1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-04 23:05:47 +02:00

Bug 393959: Unhandled template id ambiguity.

This commit is contained in:
Markus Schorn 2012-11-09 11:58:55 +01:00
parent 352eae2865
commit 73968cb5f2
4 changed files with 146 additions and 129 deletions

View file

@ -6181,4 +6181,14 @@ public class AST2TemplateTests extends AST2BaseTest {
public void testAddressAsTemplateArgument_391190() throws Exception { public void testAddressAsTemplateArgument_391190() throws Exception {
parseAndCheckBindings(getAboveComment(), CPP, true); parseAndCheckBindings(getAboveComment(), CPP, true);
} }
// template <typename T> struct CT {
// const static int const_min= 1;
// };
// void test(int off) {
// off < CT<int>::const_min || off > CT<int>::const_min;
// }
public void testTemplateIDAmbiguity_393959() throws Exception {
parseAndCheckBindings(getAboveComment(), CPP, true);
}
} }

View file

@ -17,7 +17,6 @@ import java.util.List;
import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IBinding;
@ -43,17 +42,14 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.NameOrTemplateIDVariants.Var
*/ */
public class CPPASTTemplateIDAmbiguity extends ASTAmbiguousNode implements IASTAmbiguousExpression, public class CPPASTTemplateIDAmbiguity extends ASTAmbiguousNode implements IASTAmbiguousExpression,
ICPPASTExpression { ICPPASTExpression {
private BinaryOperator fLastOperator; private final BinaryOperator fEndOperator;
private IASTInitializerClause fLastExpression;
private final BranchPoint fVariants; private final BranchPoint fVariants;
private IASTNode[] fNodes; private IASTNode[] fNodes;
private final AbstractGNUSourceCodeParser fParser; private final AbstractGNUSourceCodeParser fParser;
public CPPASTTemplateIDAmbiguity(AbstractGNUSourceCodeParser parser, BinaryOperator lastOperator, IASTInitializerClause expr, public CPPASTTemplateIDAmbiguity(AbstractGNUSourceCodeParser parser, BinaryOperator endOperator, BranchPoint variants) {
BranchPoint variants) {
fParser= parser; fParser= parser;
fLastOperator= lastOperator; fEndOperator= endOperator;
fLastExpression= expr;
fVariants= variants; fVariants= variants;
} }
@ -92,10 +88,7 @@ public class CPPASTTemplateIDAmbiguity extends ASTAmbiguousNode implements IASTA
if (selected != null) { if (selected != null) {
minOffset= selected.getRightOffset(); minOffset= selected.getRightOffset();
BinaryOperator targetOp = selected.getTargetOperator(); BinaryOperator targetOp = selected.getTargetOperator();
if (targetOp == null) { if (targetOp != null) {
fLastExpression= selected.getExpression();
fLastOperator= v.getLeftOperator();
} else {
targetOp.exchange(selected.getExpression()); targetOp.exchange(selected.getExpression());
targetOp.setNext(v.getLeftOperator()); targetOp.setNext(v.getLeftOperator());
} }
@ -106,7 +99,7 @@ public class CPPASTTemplateIDAmbiguity extends ASTAmbiguousNode implements IASTA
owner.replace(nodeToReplace, this); owner.replace(nodeToReplace, this);
// Create the expression and replace it // Create the expression and replace it
IASTExpression expr = fParser.buildExpression(fLastOperator, fLastExpression); IASTExpression expr = fParser.buildExpression(fEndOperator.getNext(), fEndOperator.getExpression());
owner.replace(this, expr); owner.replace(this, expr);
// Resolve further ambiguities within the new expression. // Resolve further ambiguities within the new expression.
@ -149,8 +142,7 @@ public class CPPASTTemplateIDAmbiguity extends ASTAmbiguousNode implements IASTA
public IASTNode[] getNodes() { public IASTNode[] getNodes() {
if (fNodes == null) { if (fNodes == null) {
List<IASTNode> nl= new ArrayList<IASTNode>(); List<IASTNode> nl= new ArrayList<IASTNode>();
nl.add(fLastExpression); BinaryOperator op= fEndOperator;
BinaryOperator op= fLastOperator;
while (op != null) { while (op != null) {
nl.add(op.getExpression()); nl.add(op.getExpression());
op= op.getNext(); op= op.getNext();

View file

@ -764,6 +764,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
BinaryOperator lastOperator= null; BinaryOperator lastOperator= null;
NameOrTemplateIDVariants variants= null; NameOrTemplateIDVariants variants= null;
IToken variantMark= mark();
if (expr == null) { if (expr == null) {
Object e = castExpressionForBinaryExpression(strat); Object e = castExpressionForBinaryExpression(strat);
if (e instanceof IASTExpression) { if (e instanceof IASTExpression) {
@ -773,20 +774,21 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
final Variant variant = (Variant) e; final Variant variant = (Variant) e;
expr= variant.getExpression(); expr= variant.getExpression();
variants.addBranchPoint(variant.getNext(), lastOperator, allowAssignment, conditionCount); variants.addBranchPoint(variant.getNext(), null, allowAssignment, conditionCount);
} }
} }
boolean doneExpression= false; boolean stopWithNextOperator= false;
do { castExprLoop: for(;;) {
// Typically after a binary operator there cannot be a throw expression // Typically after a binary operator there cannot be a throw expression
boolean allowThrow= false; boolean allowThrow= false;
// Brace initializers are allowed on the right hand side of an expression // Brace initializers are allowed on the right hand side of an expression
boolean allowBraceInitializer= false; boolean allowBraceInitializer= false;
BacktrackException tryRecovery= null; boolean doneExpression= false;
final int operatorOffset= LA().getOffset(); BacktrackException failure= null;
lt1= LT(1); final int opOffset= LA().getOffset();
lt1= stopWithNextOperator ? IToken.tSEMI : LT(1);
switch (lt1) { switch (lt1) {
case IToken.tQUESTION: case IToken.tQUESTION:
conditionCount++; conditionCount++;
@ -879,7 +881,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
if (LT(2) != IToken.tGT_in_SHIFTR) { if (LT(2) != IToken.tGT_in_SHIFTR) {
IToken token = LA(1); IToken token = LA(1);
backtrack.initialize(token.getOffset(), token.getLength()); backtrack.initialize(token.getOffset(), token.getLength());
tryRecovery= backtrack; failure= backtrack;
break; break;
} }
@ -908,34 +910,45 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
break; break;
} }
if (!doneExpression && tryRecovery == null) { // Close variants
consume(); // consumes the operator if (failure == null) {
if (doneExpression) {
// Link variants that are closed by the new operator if (variants != null && !variants.hasRightBound(opOffset)) {
if (variants != null) { // We have a longer variant, ignore this one.
variants.closeVariants(operatorOffset, lastOperator); backtrack.initialize(opOffset, 1);
failure= backtrack;
} else {
break castExprLoop;
}
} }
// Close variants with matching end
if (variants != null && lastOperator != null) {
variants.closeVariants(opOffset, lastOperator);
}
}
// Determine next sub-expression if (failure == null && !doneExpression) {
if (lt1 == IToken.tQUESTION && LT(1) == IToken.tCOLON) { // Determine next cast-expression
// Missing sub-expression after '?' (gnu-extension) consume(); // consumes the operator
expr= null; stopWithNextOperator= false;
} else if (allowThrow && LT(1) == IToken.t_throw) { try {
// Throw expression if (lt1 == IToken.tQUESTION && LT(1) == IToken.tCOLON) {
expr= throwExpression(); // Missing sub-expression after '?' (gnu-extension)
lt1= LT(1); expr= null;
if (lt1 != IToken.tCOLON && lt1 != IToken.tCOMMA) } else if (allowThrow && LT(1) == IToken.t_throw) {
// Throw expression
expr= throwExpression();
lt1= LT(1);
if (lt1 != IToken.tCOLON && lt1 != IToken.tCOMMA)
stopWithNextOperator= true;
break; break;
} else if (allowBraceInitializer && LT(1) == IToken.tLBRACE) { } else if (allowBraceInitializer && LT(1) == IToken.tLBRACE) {
// Brace initializer // Brace initializer
expr= bracedInitList(true); expr= bracedInitList(true);
lt1= LT(1); lt1= LT(1);
if (lt1 != IToken.tCOLON && lt1 != IToken.tCOMMA) if (lt1 != IToken.tCOLON && lt1 != IToken.tCOMMA)
break; stopWithNextOperator= true;
} else { } else {
// Cast expression
IToken m= mark();
try {
Object e = castExpressionForBinaryExpression(strat); Object e = castExpressionForBinaryExpression(strat);
if (e instanceof IASTExpression) { if (e instanceof IASTExpression) {
expr= (IASTExpression) e; expr= (IASTExpression) e;
@ -947,55 +960,49 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
variants.addBranchPoint(ae.getNext(), lastOperator, allowAssignment, conditionCount); variants.addBranchPoint(ae.getNext(), lastOperator, allowAssignment, conditionCount);
} }
} catch (BacktrackException e) {
if (variants == null)
throw e;
tryRecovery= e;
backup(m);
} }
continue castExprLoop;
} catch (BacktrackException e) {
failure= e;
} }
} }
if (tryRecovery != null || doneExpression) { // We need a new variant
if (variants != null) { Variant variant= variants == null ? null : variants.selectFallback();
if (lt1 == IToken.tEOC) { if (variant == null) {
variants.discardOpenVariants(operatorOffset); if (failure != null)
} else { throw failure;
// Try fall-back to an open variant throwBacktrack(LA(1));
Variant fallback= variants.findFallback(operatorOffset); } else {
if (fallback == null) { // Restore variant and continue
if (tryRecovery != null) BranchPoint varPoint= variant.getOwner();
throw tryRecovery; allowAssignment= varPoint.isAllowAssignment();
variants.discardOpenVariants(operatorOffset); conditionCount= varPoint.getConditionCount();
} else { lastOperator= varPoint.getLeftOperator();
// Restore state and continue expr= variant.getExpression();
doneExpression= false;
BranchPoint varPoint= fallback.getOwner();
allowAssignment= varPoint.isAllowAssignment();
conditionCount= varPoint.getConditionCount();
lastOperator= varPoint.getLeftOperator();
expr= fallback.getExpression();
variants.useFallback(fallback);
// Advance to the right token backup(variantMark);
int offset= fallback.getRightOffset(); int offset= variant.getRightOffset();
while (LA().getOffset() < offset) { while (LA().getOffset() < offset) {
consume(); consume();
}
}
}
} }
variantMark= mark();
} }
} while (!doneExpression); }
// Check for incomplete conditional expression // Check for incomplete conditional expression
if (lt1 != IToken.tEOC && conditionCount > 0) if (lt1 != IToken.tEOC && conditionCount > 0)
throwBacktrack(LA(1)); throwBacktrack(LA(1));
if (variants != null && !variants.isEmpty()) { if (variants != null) {
CPPASTTemplateIDAmbiguity result = new CPPASTTemplateIDAmbiguity(this, lastOperator, expr, variants.getOrderedBranchPoints()); BinaryOperator end = new BinaryOperator(lastOperator, expr, -1, 0, 0);
setRange(result, startOffset, calculateEndOffset(expr)); variants.closeVariants(LA(1).getOffset(), end);
return result; variants.removeInvalid(end);
if (!variants.isEmpty()) {
CPPASTTemplateIDAmbiguity result = new CPPASTTemplateIDAmbiguity(this, end, variants.getOrderedBranchPoints());
setRange(result, startOffset, calculateEndOffset(expr));
return result;
}
} }
return buildExpression(lastOperator, expr); return buildExpression(lastOperator, expr);
@ -1009,7 +1016,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
TemplateIdStrategy strat= new TemplateIdStrategy(); TemplateIdStrategy strat= new TemplateIdStrategy();
Variant variants= null; Variant variants= null;
IASTExpression singleExpression= null; IASTExpression singleResult= null;
IASTName[] firstNames= null; IASTName[] firstNames= null;
final IToken mark= mark(); final IToken mark= mark();
@ -1018,12 +1025,12 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
try { try {
IASTExpression e = castExpression(CastExprCtx.eDirectlyInBExpr, strat); IASTExpression e = castExpression(CastExprCtx.eDirectlyInBExpr, strat);
if (variants == null) { if (variants == null) {
if (singleExpression == null || lastToken == null) { if (singleResult == null || lastToken == null) {
singleExpression= e; singleResult= e;
firstNames= strat.getTemplateNames(); firstNames= strat.getTemplateNames();
} else { } else {
variants= new Variant(null, singleExpression, firstNames, lastToken.getOffset()); variants= new Variant(null, singleResult, firstNames, lastToken.getOffset());
singleExpression= null; singleResult= null;
firstNames= null; firstNames= null;
} }
} }
@ -1045,7 +1052,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
} }
backup(mark); backup(mark);
} }
return variants != null ? variants : singleExpression; return variants != null ? variants : singleResult;
} }
@Override @Override

View file

@ -13,7 +13,6 @@ package org.eclipse.cdt.internal.core.dom.parser.cpp;
import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser.BinaryOperator; import org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser.BinaryOperator;
/** /**
@ -132,59 +131,31 @@ public class NameOrTemplateIDVariants {
if (v.getTargetOperator() == null) { if (v.getTargetOperator() == null) {
if (offset == v.getRightOffset()) { if (offset == v.getRightOffset()) {
v.setTargetOperator(lastOperator); v.setTargetOperator(lastOperator);
} else if (offset > v.getRightOffset()) {
// Should not happen
assert false;
remove(v);
} }
} }
} }
} }
} }
public void discardOpenVariants(int operatorOffset) { public Variant selectFallback() {
for (BranchPoint p = fFirst; p != null; p= p.getNext()) {
for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) {
if (v.getTargetOperator() == null && v.getRightOffset() != operatorOffset) {
remove(v);
}
}
}
}
public Variant findFallback(int operatorOffset) {
// Search for an open variant, with a small right offset and a large left offset // Search for an open variant, with a small right offset and a large left offset
Variant best= null;
for (BranchPoint p = fFirst; p != null; p= p.getNext()) { for (BranchPoint p = fFirst; p != null; p= p.getNext()) {
Variant best= null;
for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) { for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) {
if (v.fRightOffset > operatorOffset) { if (v.getTargetOperator() == null) {
if (best == null || v.fRightOffset < best.fRightOffset) { if (best == null || v.fRightOffset < best.fRightOffset) {
best= v; best= v;
} }
} }
} }
} if (best != null) {
return best; remove(best);
} return best;
public void useFallback(Variant fallback) {
// Discard variants that end within the fallback
int begin= ((ASTNode) fallback.getExpression()).getOffset();
int end= fallback.getRightOffset();
for (BranchPoint p = fFirst; p != null; p= p.getNext()) {
for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) {
if (v == fallback) {
remove(v);
} else {
int vend= v.getRightOffset();
if (vend > begin && vend < end)
remove(v);
}
} }
} }
return null;
} }
private void remove(Variant remove) { private void remove(Variant remove) {
final BranchPoint owner = remove.fOwner; final BranchPoint owner = remove.fOwner;
final Variant next = remove.getNext(); final Variant next = remove.getNext();
@ -236,4 +207,41 @@ public class NameOrTemplateIDVariants {
fFirst= null; fFirst= null;
return prev; return prev;
} }
public boolean hasRightBound(int opOffset) {
// Search for an open variant, with a small right offset and a large left offset
for (BranchPoint p = fFirst; p != null; p= p.getNext()) {
for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) {
if (v.fRightOffset > opOffset)
return false;
}
}
return true;
}
public void removeInvalid(BinaryOperator lastOperator) {
for (BranchPoint p = fFirst; p != null; p= p.getNext()) {
if (!isReachable(p, lastOperator)) {
remove(p);
} else {
for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) {
if (v.getTargetOperator() == null) {
remove(v);
}
}
}
}
}
private boolean isReachable(BranchPoint bp, BinaryOperator endOperator) {
BinaryOperator op = bp.getLeftOperator();
if (op == null)
return true;
for(; endOperator != null; endOperator= endOperator.getNext()) {
if (endOperator == op)
return true;
}
return false;
}
} }