1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-10 09:45:39 +02:00

Add c++17 fold expression

This commit is contained in:
Igor V. Kovalenko 2022-12-09 21:07:58 +03:00 committed by Jonah Graham
parent eb083f8cdd
commit f2f862351e
11 changed files with 1096 additions and 7 deletions

View file

@ -0,0 +1,125 @@
/*******************************************************************************
* Copyright (c) 2023 Igor V. Kovalenko.
*
* 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
*
* Contributors:
* Igor V. Kovalenko - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2.cxx17;
import static org.eclipse.cdt.core.parser.ParserLanguage.CPP;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTProblemStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.parser.tests.ast2.AST2CPPTestBase;
/**
* AST tests for C++17 fold expressions.
*/
public class FoldExpressionTests extends AST2CPPTestBase {
// using size_t = decltype(sizeof(int));
//
// template<typename T> struct X {
// static constexpr size_t g(const T& arg) noexcept { return sizeof(arg); }
// };
//
// template<typename... Pack>
// constexpr size_t f1(const Pack&... pack) { return (... + X<Pack>::g(pack)); }
// template<typename... Pack>
// constexpr size_t f2(const Pack&... pack) { return (0 + ... + X<Pack>::g(pack)); }
// template<typename... Pack>
// constexpr size_t f3(const Pack&... pack) { return (X<Pack>::g(pack) + ...); }
// template<typename... Pack>
// constexpr size_t f4(const Pack&... pack) { return (X<Pack>::g(pack) + ... + 0); }
//
// static constexpr auto val1 = f1(1, 2., "1");
// static constexpr auto val2 = f2(1, 2., "12");
// static constexpr auto val3 = f3(1, 2., "123");
// static constexpr auto val4 = f4(1, 2., "1234");
public void testFoldExpression1() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
helper.assertVariableValue("val1", 14);
helper.assertVariableValue("val2", 15);
helper.assertVariableValue("val3", 16);
helper.assertVariableValue("val4", 17);
}
// template<typename... Pack>
// constexpr bool f1(const Pack&... pack) { return (... && pack); }
// template<typename... Pack>
// constexpr bool f2(const Pack&... pack) { return (pack && ...); }
// template<typename... Pack>
// constexpr bool f3(const Pack&... pack) { return (... || pack); }
// template<typename... Pack>
// constexpr bool f4(const Pack&... pack) { return (pack || ...); }
//
// static constexpr auto val1 = f1();
// static constexpr auto val21 = f2(false);
// static constexpr auto val22 = f2(true);
// static constexpr auto val3 = f3();
// static constexpr auto val41 = f4(false);
// static constexpr auto val42 = f4(true);
public void testFoldExpression2() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
helper.assertVariableValue("val1", 1);
helper.assertVariableValue("val21", 0);
helper.assertVariableValue("val22", 1);
helper.assertVariableValue("val3", 0);
helper.assertVariableValue("val41", 0);
helper.assertVariableValue("val42", 1);
}
// template <typename CharT>
// struct ostream {
// template <typename T>
// ostream& operator<<(T);
//
// ostream& operator<<(ostream&(*)(ostream&));
// };
//
// template <typename CharT>
// ostream<CharT>& endl(ostream<CharT>&);
//
// template <typename... T>
// void sum(T... vals) {
// ostream<char> out;
// out << (... + vals) << endl;
// }
public void testFoldExpressionInBinaryExpression() throws Exception {
parseAndCheckBindings();
}
// template<typename... T>
// void sum(T... vals) {
// bar(... + vals);
// }
public void testFoldExpressionRecognition1() throws Exception {
final String code = getAboveComment();
IASTTranslationUnit tu = parse(code, CPP, false, false);
ICPPASTTemplateDeclaration tdef = getDeclaration(tu, 0);
IASTFunctionDefinition fdef = (IASTFunctionDefinition) tdef.getDeclaration();
IASTProblemStatement p1 = getStatement(fdef, 0);
}
// template<typename... T>
// void sum(T... vals) {
// ... + vals;
// }
public void testFoldExpressionRecognition2() throws Exception {
final String code = getAboveComment();
IASTTranslationUnit tu = parse(code, CPP, false, false);
ICPPASTTemplateDeclaration tdef = getDeclaration(tu, 0);
IASTFunctionDefinition fdef = (IASTFunctionDefinition) tdef.getDeclaration();
IASTProblemStatement p1 = getStatement(fdef, 0);
}
}

View file

@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (c) 2022 Igor V. Kovalenko.
*
* 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
*
* Contributors:
* Igor V. Kovalenko - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.dom.ast.cpp;
/**
* Fold expression, introduced in C++17.
*
* @since 8.0
* @noextend This interface is not intended to be extended by clients.
* @noimplement This interface is not intended to be implemented by clients.
*/
public interface ICPPASTFoldExpression extends ICPPASTExpression {
}

View file

@ -242,6 +242,17 @@ public interface ICPPNodeFactory extends INodeFactory {
*/
public ICPPASTLambdaExpression newLambdaExpression();
/**
* @since 8.0
*/
public IASTExpression newFoldExpressionToken();
/**
* @since 8.0
*/
public ICPPASTFoldExpression newFoldExpression(int opToken, boolean isComma, IASTExpression lhs,
IASTExpression rhs);
public ICPPASTLinkageSpecification newLinkageSpecification(String literal);
@Override

View file

@ -1010,9 +1010,13 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
public void setNext(BinaryOperator next) {
fNext = next;
}
public int getOperatorToken() {
return fOperatorToken;
}
}
public final IASTExpression buildExpression(BinaryOperator leftChain, IASTInitializerClause expr) {
public IASTExpression buildExpression(BinaryOperator leftChain, IASTInitializerClause expr) {
BinaryOperator rightChain = null;
for (;;) {
if (leftChain == null) {

View file

@ -45,7 +45,7 @@ public interface ITypeMarshalBuffer {
EVAL_FUNCTION_SET = 0x09, EVAL_ID = 0x0A, EVAL_INIT_LIST = 0x0B, EVAL_MEMBER_ACCESS = 0x0C,
EVAL_PACK_EXPANSION = 0x0D, EVAL_TYPE_ID = 0x0E, EVAL_UNARY = 0x0F, EVAL_UNARY_TYPE_ID = 0x10,
EVAL_CONSTRUCTOR = 0x11, EVAL_REFERENCE = 0x12, EVAL_POINTER = 0x13, EVAL_COMPOSITE_ACCESS = 0x14,
EVAL_NARY_TYPE_ID = 0x15, EVAL_PACK_ACCESS = 0x16;
EVAL_NARY_TYPE_ID = 0x15, EVAL_PACK_ACCESS = 0x16, EVAL_FOLD_EXPRESSION = 0x17;
// Can add more evaluations up to 0x1C, after that it will collide with TypeMarshalBuffer.UNSTORABLE_TYPE.
final static byte EXEC_COMPOUND_STATEMENT = 0x01, EXEC_BREAK = 0x02, EXEC_CASE = 0x03, EXEC_CONTINUE = 0x04,

View file

@ -0,0 +1,232 @@
/*******************************************************************************
* Copyright (c) 2022 Igor V. Kovalenko.
*
* 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
*
* Contributors:
* Igor V. Kovalenko - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.LVALUE;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTImplicitDestructorName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFoldExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTPackExpansionExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameterPackType;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.DestructorCallCollector;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFixed;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFoldExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalPackExpansion;
/**
* Implementation for fold expressions.
*/
public class CPPASTFoldExpression extends ASTNode implements ICPPASTFoldExpression, IASTAmbiguityParent {
private final int fOperator;
private final boolean fIsComma;
private ICPPASTExpression fLhs;
private ICPPASTExpression fRhs;
private ICPPEvaluation fEvaluation;
private IASTImplicitDestructorName[] fImplicitDestructorNames;
public CPPASTFoldExpression(int operator, boolean isComma, IASTExpression lhs, IASTExpression rhs) {
fOperator = operator;
fIsComma = isComma;
setOperand1(lhs);
setOperand2(rhs);
}
private void setOperand1(IASTExpression expression) {
assertNotFrozen();
if (expression != null) {
if (!(expression instanceof ICPPASTExpression)) {
throw new IllegalArgumentException(expression.getClass().getName());
}
expression.setParent(this);
}
fLhs = (ICPPASTExpression) expression;
}
public void setOperand2(IASTExpression operand) {
assertNotFrozen();
if (operand != null) {
if (!(operand instanceof ICPPASTExpression)) {
throw new IllegalArgumentException(operand.getClass().getName());
}
operand.setParent(this);
}
fRhs = (ICPPASTExpression) operand;
}
@Override
public IASTExpression copy() {
return copy(CopyStyle.withoutLocations);
}
@Override
public IASTExpression copy(CopyStyle style) {
IASTExpression fLhsCopy = fLhs == null ? null : fLhs.copy(style);
IASTExpression fRhsCopy = fRhs == null ? null : fRhs.copy(style);
CPPASTFoldExpression copy = new CPPASTFoldExpression(fOperator, fIsComma, fLhsCopy, fRhsCopy);
return copy(copy, style);
}
@Override
public ICPPEvaluation getEvaluation() {
if (fEvaluation == null) {
fEvaluation = computeEvaluation();
}
return fEvaluation;
}
private final class UnexpandedParameterPackCounter extends ASTVisitor {
int count;
public UnexpandedParameterPackCounter() {
super(false);
shouldVisitExpressions = true;
count = 0;
}
public int getCount() {
return count;
}
@Override
public int visit(IASTExpression expression) {
if (expression instanceof ICPPASTPackExpansionExpression) {
return PROCESS_SKIP;
}
IType type = expression.getExpressionType();
if (type instanceof ICPPParameterPackType) {
++count;
}
return PROCESS_CONTINUE;
}
}
private int countUnexpandedParameterPacks(IASTExpression e) {
if (e == null) {
return 0;
}
UnexpandedParameterPackCounter counter = new UnexpandedParameterPackCounter();
e.accept(counter);
return counter.getCount();
}
private ICPPEvaluation computeEvaluation() {
int lhsParameterPackCount = countUnexpandedParameterPacks(fLhs);
int rhsParameterPackCount = countUnexpandedParameterPacks(fRhs);
// Either left or right hand side expression shall contain an unexpanded parameter pack,
// but not both.
if (!((lhsParameterPackCount != 0) ^ (rhsParameterPackCount != 0))) {
return EvalFixed.INCOMPLETE;
}
ICPPEvaluation packEval;
ICPPEvaluation initEval;
boolean isLeftFold;
ICPPEvaluation evalL = fLhs == null ? null : fLhs.getEvaluation();
ICPPEvaluation evalR = fRhs == null ? null : fRhs.getEvaluation();
if (lhsParameterPackCount == 0) {
isLeftFold = true;
initEval = evalL;
packEval = evalR;
} else {
isLeftFold = false;
initEval = evalR;
packEval = evalL;
}
ICPPEvaluation[] foldPattern = new ICPPEvaluation[] { new EvalPackExpansion(packEval, this) };
return new EvalFoldExpression(fOperator, fIsComma, isLeftFold, foldPattern, initEval, this);
}
@Override
public IType getExpressionType() {
return CPPEvaluation.getType(this);
}
@Override
public boolean isLValue() {
return getValueCategory() == LVALUE;
}
@Override
public ValueCategory getValueCategory() {
return CPPEvaluation.getValueCategory(this);
}
@Override
public IASTImplicitDestructorName[] getImplicitDestructorNames() {
if (fImplicitDestructorNames == null) {
fImplicitDestructorNames = DestructorCallCollector.getTemporariesDestructorCalls(this);
}
return fImplicitDestructorNames;
}
@Override
public boolean accept(ASTVisitor action) {
if (action.shouldVisitExpressions) {
switch (action.visit(this)) {
case ASTVisitor.PROCESS_ABORT:
return false;
case ASTVisitor.PROCESS_SKIP:
return true;
default:
break;
}
}
if (fLhs != null && !fLhs.accept(action)) {
return false;
}
if (fRhs != null && !fRhs.accept(action)) {
return false;
}
if (action.shouldVisitExpressions && action.leave(this) == ASTVisitor.PROCESS_ABORT) {
return false;
}
return true;
}
@Override
public void replace(IASTNode child, IASTNode other) {
if (child == fLhs) {
other.setPropertyInParent(child.getPropertyInParent());
other.setParent(child.getParent());
fLhs = (ICPPASTExpression) other;
}
if (child == fRhs) {
other.setPropertyInParent(child.getPropertyInParent());
other.setParent(child.getParent());
fRhs = (ICPPASTExpression) other;
}
}
}

View file

@ -0,0 +1,49 @@
/*******************************************************************************
* Copyright (c) 2023 Igor V. Kovalenko.
*
* 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
*
* Contributors:
* Igor V. Kovalenko - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
/**
* Represents <code>...</code> token in fold expression.
*/
public class CPPASTFoldExpressionToken extends ASTNode implements IASTExpression {
@Override
public IType getExpressionType() {
return null;
}
@Override
public boolean isLValue() {
return false;
}
@Override
public ValueCategory getValueCategory() {
return null;
}
@Override
public IASTExpression copy() {
return null;
}
@Override
public IASTExpression copy(CopyStyle style) {
return null;
}
}

View file

@ -88,6 +88,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExpressionList;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldDesignator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFoldExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTForStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
@ -565,6 +566,17 @@ public class CPPNodeFactory extends NodeFactory implements ICPPNodeFactory {
return new CPPASTLambdaExpression();
}
@Override
public IASTExpression newFoldExpressionToken() {
return new CPPASTFoldExpressionToken();
}
@Override
public ICPPASTFoldExpression newFoldExpression(int operator, boolean fIsComma, IASTExpression lhs,
IASTExpression rhs) {
return new CPPASTFoldExpression(operator, fIsComma, lhs, rhs);
}
@Override
public ICPPASTLinkageSpecification newLinkageSpecification(String literal) {
return new CPPASTLinkageSpecification(literal);

View file

@ -64,6 +64,7 @@ import org.eclipse.cdt.core.dom.ast.IASTPointer;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTProblemDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTProblemExpression;
import org.eclipse.cdt.core.dom.ast.IASTProblemTypeId;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
@ -104,6 +105,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldDesignator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFoldExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTForStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator.RefQualifier;
@ -206,6 +208,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
private final boolean supportAutoTypeSpecifier;
private final boolean supportUserDefinedLiterals;
private final boolean supportGCCStyleDesignators;
private final boolean supportFoldExpression;
private final IIndex index;
protected ICPPASTTranslationUnit translationUnit;
@ -243,6 +246,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
scanner.setSplitShiftROperator(true);
fContextSensitiveTokens = createContextSensitiveTokenMap(config);
additionalNumericalSuffixes = scanner.getAdditionalNumericLiteralSuffixes();
supportFoldExpression = true;
}
@Override
@ -984,7 +988,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
* else where.
*/
private enum BinaryExprCtx {
eInTemplateID, eNotInTemplateID
eInTemplateID, eNotInTemplateID, eInPrimaryExpression
}
@Override
@ -1014,6 +1018,14 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
IToken variantMark = mark();
if (expr == null) {
// Could be ellipsis of unary left fold expression
IASTExpression foldExpression = foldStartingExpression(ctx, strat);
if (foldExpression != null) {
lt1 = LT(1);
lastOperator = new BinaryOperator(lastOperator, foldExpression, lt1, 0, 0);
consume(); // consume operator token
}
Object e = castExpressionForBinaryExpression(strat);
if (e instanceof IASTExpression) {
expr = (IASTExpression) e;
@ -1022,7 +1034,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
final Variant variant = (Variant) e;
expr = variant.getExpression();
variants.addBranchPoint(variant.getNext(), null, allowAssignment, conditionCount);
variants.addBranchPoint(variant.getNext(), lastOperator, allowAssignment, conditionCount);
}
}
@ -1199,7 +1211,12 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
if (lt1 != IToken.tCOLON && lt1 != IToken.tCOMMA)
stopWithNextOperator = true;
} else {
Object e = castExpressionForBinaryExpression(strat);
// Could be ellipsis of any right fold expression or ellipsis of binary left fold expression
Object e = foldInsideExpression(ctx, strat, lt1);
if (e == null) {
e = castExpressionForBinaryExpression(strat);
}
if (e instanceof IASTExpression) {
expr = (IASTExpression) e;
} else {
@ -1259,6 +1276,134 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
return (ICPPASTExpression) buildExpression(lastOperator, expr);
}
private int calculateFirstOffset(BinaryOperator leftChain, IASTInitializerClause expr) {
int firstOffset = ((ASTNode) expr).getOffset();
while (leftChain != null) {
expr = leftChain.getExpression();
if (expr != null) {
int exprOffset = ((ASTNode) expr).getOffset();
if (firstOffset > exprOffset) {
firstOffset = exprOffset;
}
}
leftChain = leftChain.getNext();
}
return firstOffset;
}
@Override
public final IASTExpression buildExpression(BinaryOperator leftChain, IASTInitializerClause expr) {
if (supportFoldExpression && leftChain != null && expr != null) {
int foldCount = 0;
int foldOpToken = 0;
int firstOffset = calculateFirstOffset(leftChain, expr);
int endOffset = calculateEndOffset(expr);
if (expr instanceof CPPASTFoldExpressionToken) {
// unary right fold: (pack op ...)
++foldCount;
}
BinaryOperator prev = null;
BinaryOperator foldOp = null;
BinaryOperator foldOpPrev = null;
scanFoldExpressions: for (BinaryOperator op = leftChain; op != null; op = op.getNext()) {
if (op.getExpression() instanceof CPPASTFoldExpressionToken) {
if (++foldCount == 1) {
foldOp = op;
foldOpPrev = prev;
} else {
// only single fold token allowed
foldOp = null;
foldOpPrev = null;
break scanFoldExpressions;
}
} else {
prev = op;
}
}
if (foldCount == 1) {
BinaryOperator rightChain;
if (foldOp == null) {
// unary right fold, remove expression and use left chain as is
foldOpToken = leftChain.getOperatorToken();
expr = null;
rightChain = null;
} else {
foldOpToken = foldOp.getOperatorToken();
if (foldOpPrev != null) {
// if fold token is not the rightmost one in original chain,
// break the chain and move front part to the right
foldOpPrev.setNext(null);
rightChain = leftChain;
} else {
rightChain = null;
}
// move tail part to the left
leftChain = foldOp.getNext();
}
IASTExpression lhs = leftChain == null ? null
: super.buildExpression(leftChain.getNext(), leftChain.getExpression());
IASTExpression rhs = super.buildExpression(rightChain, expr);
return buildFoldExpression(foldOpToken, lhs, rhs, firstOffset, endOffset);
} else if (foldCount > 1) {
IASTProblem problem = createProblem(IProblem.SYNTAX_ERROR, firstOffset, endOffset - firstOffset);
IASTProblemExpression pexpr = getNodeFactory().newProblemExpression(problem);
((ASTNode) pexpr).setOffsetAndLength(((ASTNode) problem));
return pexpr;
}
}
return super.buildExpression(leftChain, expr);
}
private IASTExpression foldStartingExpression(final BinaryExprCtx ctx, ITemplateIdStrategy strat)
throws EndOfFileException, BacktrackException {
if (supportFoldExpression && ctx == BinaryExprCtx.eInPrimaryExpression) {
if (LTcatchEOF(1) == IToken.tELLIPSIS) {
int rightOpToken = LTcatchEOF(2);
if (allowedFoldExpressionOpToken(rightOpToken)) {
// unary left fold expression: (... op pack)
IToken foldToken = consume();
return buildFoldExpressionToken(foldToken.getOffset(), foldToken.getEndOffset());
}
}
}
return null;
}
private IASTExpression foldInsideExpression(final BinaryExprCtx ctx, ITemplateIdStrategy strat, int leftOpToken)
throws EndOfFileException, BacktrackException {
if (supportFoldExpression && ctx == BinaryExprCtx.eInPrimaryExpression) {
if (LTcatchEOF(1) == IToken.tELLIPSIS) {
if (allowedFoldExpressionOpToken(leftOpToken)) {
int rightOpToken = LTcatchEOF(2);
if (rightOpToken == 0 || rightOpToken == IToken.tRPAREN || rightOpToken == leftOpToken) {
// unary right fold: (... op pack)
// or
// binary right fold: (pack op ... op init)
// binary left fold: (init op ... op pack)
IToken foldToken = consume();
return buildFoldExpressionToken(foldToken.getOffset(), foldToken.getEndOffset());
}
}
}
}
return null;
}
public Object castExpressionForBinaryExpression(ITemplateIdStrategy s)
throws EndOfFileException, BacktrackException {
if (s != null) {
@ -1315,6 +1460,124 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
return result;
}
private IASTExpression buildFoldExpressionToken(int firstOffset, int lastOffset) {
IASTExpression result = getNodeFactory().newFoldExpressionToken();
((ASTNode) result).setOffsetAndLength(firstOffset, lastOffset - firstOffset);
return result;
}
private ICPPASTFoldExpression buildFoldExpression(int opToken, IASTExpression expr1, IASTExpression expr2,
int firstOffset, int lastOffset) {
int op = 0;
boolean isComma = false;
switch (opToken) {
case IToken.tPLUS:
op = IASTBinaryExpression.op_plus;
break;
case IToken.tMINUS:
op = IASTBinaryExpression.op_minus;
break;
case IToken.tSTAR:
op = IASTBinaryExpression.op_multiply;
break;
case IToken.tDIV:
op = IASTBinaryExpression.op_divide;
break;
case IToken.tMOD:
op = IASTBinaryExpression.op_modulo;
break;
case IToken.tXOR:
op = IASTBinaryExpression.op_binaryXor;
break;
case IToken.tAMPER:
op = IASTBinaryExpression.op_binaryAnd;
break;
case IToken.tBITOR:
op = IASTBinaryExpression.op_binaryOr;
break;
case IToken.tASSIGN:
op = IASTBinaryExpression.op_assign;
break;
case IToken.tLT:
op = IASTBinaryExpression.op_lessThan;
break;
case IToken.tGT:
op = IASTBinaryExpression.op_greaterThan;
break;
case IToken.tSHIFTL:
op = IASTBinaryExpression.op_shiftLeft;
break;
case IToken.tSHIFTR:
op = IASTBinaryExpression.op_shiftRight;
break;
case IToken.tPLUSASSIGN:
op = IASTBinaryExpression.op_plusAssign;
break;
case IToken.tMINUSASSIGN:
op = IASTBinaryExpression.op_minusAssign;
break;
case IToken.tSTARASSIGN:
op = IASTBinaryExpression.op_multiplyAssign;
break;
case IToken.tDIVASSIGN:
op = IASTBinaryExpression.op_divideAssign;
break;
case IToken.tMODASSIGN:
op = IASTBinaryExpression.op_moduloAssign;
break;
case IToken.tXORASSIGN:
op = IASTBinaryExpression.op_binaryXorAssign;
break;
case IToken.tAMPERASSIGN:
op = IASTBinaryExpression.op_binaryAndAssign;
break;
case IToken.tBITORASSIGN:
op = IASTBinaryExpression.op_binaryOrAssign;
break;
case IToken.tSHIFTLASSIGN:
op = IASTBinaryExpression.op_shiftLeftAssign;
break;
case IToken.tSHIFTRASSIGN:
op = IASTBinaryExpression.op_shiftRightAssign;
break;
case IToken.tEQUAL:
op = IASTBinaryExpression.op_equals;
break;
case IToken.tNOTEQUAL:
op = IASTBinaryExpression.op_notequals;
break;
case IToken.tLTEQUAL:
op = IASTBinaryExpression.op_lessEqual;
break;
case IToken.tGTEQUAL:
op = IASTBinaryExpression.op_greaterEqual;
break;
case IToken.tAND:
op = IASTBinaryExpression.op_logicalAnd;
break;
case IToken.tOR:
op = IASTBinaryExpression.op_logicalOr;
break;
case IToken.tCOMMA:
isComma = true;
break;
case IToken.tDOTSTAR:
op = IASTBinaryExpression.op_pmdot;
break;
case IToken.tARROWSTAR:
op = IASTBinaryExpression.op_pmarrow;
break;
default:
return null;
}
ICPPASTFoldExpression result = getNodeFactory().newFoldExpression(op, isComma, expr1, expr2);
((ASTNode) result).setOffsetAndLength(firstOffset, lastOffset - firstOffset);
return result;
}
private ICPPASTExpression throwExpression() throws EndOfFileException, BacktrackException {
IToken throwToken = consume();
IASTExpression throwExpression = null;
@ -2016,12 +2279,13 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
literalExpr = getNodeFactory().newLiteralExpression(IASTLiteralExpression.lk_this, t.getImage());
return setRange(literalExpr, t.getOffset(), t.getEndOffset());
case IToken.tLPAREN:
// ( expression ) or fold-expression
if (supportStatementsInExpressions && LT(2) == IToken.tLBRACE) {
return compoundStatementExpression();
}
t = consume();
int finalOffset = 0;
IASTExpression lhs = expression(ExprKind.eExpression, BinaryExprCtx.eNotInTemplateID, null, null); // instead of expression(), to keep the stack smaller
IASTExpression lhs = expression(ExprKind.eExpression, BinaryExprCtx.eInPrimaryExpression, null, null); // instead of expression(), to keep the stack smaller
switch (LT(1)) {
case IToken.tRPAREN:
case IToken.tEOC:
@ -2030,7 +2294,11 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
default:
throwBacktrack(LA(1));
}
return buildUnaryExpression(IASTUnaryExpression.op_bracketedPrimary, lhs, t.getOffset(), finalOffset);
if (lhs instanceof ICPPASTFoldExpression) {
return setRange(lhs, t.getOffset(), finalOffset);
} else {
return buildUnaryExpression(IASTUnaryExpression.op_bracketedPrimary, lhs, t.getOffset(), finalOffset);
}
case IToken.tIDENTIFIER:
case IToken.tCOLONCOLON:
case IToken.t_operator:
@ -2090,6 +2358,47 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
return setRange(r, t.getOffset(), t.getEndOffset());
}
private boolean allowedFoldExpressionOpToken(int opToken) {
switch (opToken) {
case IToken.tPLUS:
case IToken.tMINUS:
case IToken.tSTAR:
case IToken.tDIV:
case IToken.tMOD:
case IToken.tXOR:
case IToken.tAMPER:
case IToken.tBITOR:
case IToken.tASSIGN:
case IToken.tLT:
case IToken.tGT:
case IToken.tSHIFTL:
case IToken.tSHIFTR:
case IToken.tPLUSASSIGN:
case IToken.tMINUSASSIGN:
case IToken.tSTARASSIGN:
case IToken.tDIVASSIGN:
case IToken.tMODASSIGN:
case IToken.tXORASSIGN:
case IToken.tAMPERASSIGN:
case IToken.tBITORASSIGN:
case IToken.tSHIFTLASSIGN:
case IToken.tSHIFTRASSIGN:
case IToken.tEQUAL:
case IToken.tNOTEQUAL:
case IToken.tLTEQUAL:
case IToken.tGTEQUAL:
case IToken.tAND:
case IToken.tOR:
case IToken.tCOMMA:
case IToken.tDOTSTAR:
case IToken.tARROWSTAR:
return true;
default:
return false;
}
}
private IASTExpression lambdaExpression() throws EndOfFileException, BacktrackException {
final int offset = LA().getOffset();

View file

@ -0,0 +1,318 @@
/*******************************************************************************
* Copyright (c) 2022 Igor V. Kovalenko.
*
* 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
*
* Contributors:
* Igor V. Kovalenko - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.PRVALUE;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameterMap;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer;
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.InstantiationContext;
import org.eclipse.core.runtime.CoreException;
public class EvalFoldExpression extends CPPDependentEvaluation {
private static final EvalFixed EVAL_TRUE = new EvalFixed(CPPBasicType.BOOLEAN, PRVALUE, IntegralValue.create(true));
private static final EvalFixed EVAL_FALSE = new EvalFixed(CPPBasicType.BOOLEAN, PRVALUE,
IntegralValue.create(false));
/*private static final EvalFixed EVAL_VOID = new EvalFixed(CPPBasicType.VOID, PRVALUE,
IntegralValue.create(0));*/
private final int fOperator;
private final boolean fIsComma;
private final boolean fIsLeftFold;
private ICPPEvaluation[] fPackEvals;
private ICPPEvaluation fInitEval;
private IType fType;
private boolean fCheckedIsConstantExpression;
private boolean fIsConstantExpression;
private ICPPEvaluation fEvaluation;
public EvalFoldExpression(int operator, boolean isComma, boolean isLeftFold, ICPPEvaluation[] packEvals,
ICPPEvaluation initEval, IASTNode pointOfDefinition) {
this(operator, isComma, isLeftFold, packEvals, initEval, findEnclosingTemplate(pointOfDefinition));
}
public EvalFoldExpression(int operator, boolean isComma, boolean isLeftFold, ICPPEvaluation[] packEvals,
ICPPEvaluation initEval, IBinding templateDefinition) {
super(templateDefinition);
fOperator = operator;
fIsComma = isComma;
fIsLeftFold = isLeftFold;
fPackEvals = packEvals;
fInitEval = initEval;
}
public int getOperator() {
return fOperator;
}
public ICPPEvaluation getInitExpression() {
return fInitEval;
}
@Override
public boolean isInitializerList() {
return false;
}
@Override
public boolean isFunctionSet() {
return false;
}
@Override
public boolean isTypeDependent() {
if (fType != null) {
return fType instanceof TypeOfDependentExpression;
}
return containsDependentType(fPackEvals) || (fInitEval != null && fInitEval.isTypeDependent());
}
@Override
public boolean isValueDependent() {
return containsDependentValue(fPackEvals) || (fInitEval != null && fInitEval.isValueDependent());
}
@Override
public boolean isConstantExpression() {
if (!fCheckedIsConstantExpression) {
fCheckedIsConstantExpression = true;
fIsConstantExpression = computeIsConstantExpression();
}
return fIsConstantExpression;
}
@Override
public boolean isEquivalentTo(ICPPEvaluation other) {
if (!(other instanceof EvalFoldExpression)) {
return false;
}
EvalFoldExpression o = (EvalFoldExpression) other;
return fOperator == o.fOperator && fIsComma == o.fIsComma && fIsLeftFold == o.fIsLeftFold
&& fPackEvals == o.fPackEvals
&& (fInitEval == null ? o.fInitEval == null : fInitEval.isEquivalentTo(o.fInitEval));
}
private boolean computeIsConstantExpression() {
return areAllConstantExpressions(fPackEvals) && (fInitEval == null || fInitEval.isConstantExpression());
}
@Override
public IType getType() {
if (fType == null) {
if (isTypeDependent()) {
fType = new TypeOfDependentExpression(this);
} else {
fType = computeEvaluation().getType();
}
}
return fType;
}
@Override
public IValue getValue() {
ICPPEvaluation evaluation = computeEvaluation();
return evaluation.getValue();
}
private ICPPEvaluation computeEvaluation() {
if (fEvaluation == null) {
if (fInitEval == null && fPackEvals.length == 0) {
// unary fold with empty pack
if (fIsComma) {
// expression: void(), cannot evaluate
fEvaluation = EvalFixed.INCOMPLETE;
} else if (fOperator == IASTBinaryExpression.op_logicalAnd) {
// expression: true
fEvaluation = EVAL_TRUE;
} else if (fOperator == IASTBinaryExpression.op_logicalOr) {
// expression: false
fEvaluation = EVAL_FALSE;
} else {
// error, cannot evaluate
fEvaluation = EvalFixed.INCOMPLETE;
}
} else {
// For right fold the expanded pack array is already reversed by instantiate()
if (fIsComma) {
int offset = 0;
ICPPEvaluation[] evals;
if (fInitEval != null) {
evals = new ICPPEvaluation[fPackEvals.length + 1];
if (fIsLeftFold) {
evals[0] = fInitEval;
offset = 1;
} else {
evals[fPackEvals.length] = fInitEval;
offset = 0;
}
} else {
evals = new ICPPEvaluation[fPackEvals.length];
offset = 0;
}
for (ICPPEvaluation packElement : fPackEvals) {
evals[offset++] = packElement;
}
fEvaluation = new EvalComma(evals, getTemplateDefinition());
} else {
ICPPEvaluation folded = fInitEval;
for (ICPPEvaluation packElement : fPackEvals) {
if (folded == null) {
folded = packElement;
} else {
if (fIsLeftFold) {
folded = new EvalBinary(fOperator, folded, packElement, getTemplateDefinition());
} else {
folded = new EvalBinary(fOperator, packElement, folded, getTemplateDefinition());
}
}
}
fEvaluation = folded;
}
}
}
return fEvaluation;
}
@Override
public ValueCategory getValueCategory() {
return ValueCategory.PRVALUE;
}
@Override
public void marshal(ITypeMarshalBuffer buffer, boolean includeValue) throws CoreException {
short firstBytes = ITypeMarshalBuffer.EVAL_FOLD_EXPRESSION;
if (fIsComma) {
firstBytes |= ITypeMarshalBuffer.FLAG1;
}
if (fIsLeftFold) {
firstBytes |= ITypeMarshalBuffer.FLAG2;
}
buffer.putShort((byte) firstBytes);
buffer.putInt(fOperator);
buffer.putInt(fPackEvals.length);
for (ICPPEvaluation arg : fPackEvals) {
buffer.marshalEvaluation(arg, includeValue);
}
buffer.marshalEvaluation(fInitEval, includeValue);
marshalTemplateDefinition(buffer);
}
public static ICPPEvaluation unmarshal(short firstBytes, ITypeMarshalBuffer buffer) throws CoreException {
boolean isComma = (firstBytes & ITypeMarshalBuffer.FLAG1) != 0;
boolean isLeftFold = (firstBytes & ITypeMarshalBuffer.FLAG2) != 0;
int operator = buffer.getInt();
int len = buffer.getInt();
ICPPEvaluation[] packEvals = new ICPPEvaluation[len];
for (int i = 0; i < packEvals.length; i++) {
packEvals[i] = buffer.unmarshalEvaluation();
}
ICPPEvaluation initEval = buffer.unmarshalEvaluation();
IBinding templateDefinition = buffer.unmarshalBinding();
return new EvalFoldExpression(operator, isComma, isLeftFold, packEvals, initEval, templateDefinition);
}
@Override
public ICPPEvaluation instantiate(InstantiationContext context, int maxDepth) {
ICPPEvaluation[] packEvals = instantiateExpressions(fPackEvals, context, maxDepth);
ICPPEvaluation initEval = fInitEval == null ? null : fInitEval.instantiate(context, maxDepth);
if (packEvals == fPackEvals && initEval == fInitEval) {
return this;
}
if (!fIsLeftFold) {
ArrayUtil.reverse(packEvals);
}
return new EvalFoldExpression(fOperator, fIsComma, fIsLeftFold, packEvals, initEval, getTemplateDefinition());
}
@Override
public ICPPEvaluation computeForFunctionCall(ActivationRecord record, ConstexprEvaluationContext context) {
if (context.getStepsPerformed() >= ConstexprEvaluationContext.MAX_CONSTEXPR_EVALUATION_STEPS) {
return EvalFixed.INCOMPLETE;
}
ICPPEvaluation[] packEvals = new ICPPEvaluation[fPackEvals.length];
for (int i = 0; i < fPackEvals.length; i++) {
ICPPEvaluation arg = fPackEvals[i].computeForFunctionCall(record, context.recordStep());
packEvals[i] = arg;
}
ICPPEvaluation initEval = fInitEval == null ? null
: fInitEval.computeForFunctionCall(record, context.recordStep());
if (packEvals == fPackEvals && initEval == fInitEval) {
return this;
}
return new EvalFoldExpression(fOperator, fIsComma, fIsLeftFold, packEvals, initEval, getTemplateDefinition());
}
@Override
public int determinePackSize(ICPPTemplateParameterMap tpMap) {
int r = CPPTemplates.PACK_SIZE_NOT_FOUND;
for (ICPPEvaluation packElement : fPackEvals) {
r = CPPTemplates.combinePackSize(r, packElement.determinePackSize(tpMap));
}
return r;
}
@Override
public boolean referencesTemplateParameter() {
for (ICPPEvaluation arg : fPackEvals) {
if (arg.referencesTemplateParameter()) {
return true;
}
}
return fInitEval != null && fInitEval.referencesTemplateParameter();
}
@Override
public boolean isNoexcept() {
for (int i = 0; i < fPackEvals.length; i++) {
ICPPEvaluation eval = fPackEvals[i];
if (!eval.isNoexcept()) {
return false;
}
}
if (fInitEval != null) {
return fInitEval.isNoexcept();
}
return true;
}
}

View file

@ -130,6 +130,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalCompoundStatem
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalConditional;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalConstructor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFixed;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFoldExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFunctionCall;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFunctionSet;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalID;
@ -1729,6 +1730,8 @@ class PDOMCPPLinkage extends PDOMLinkage implements IIndexCPPBindingConstants {
return EvalNaryTypeId.unmarshal(firstBytes, buffer);
case ITypeMarshalBuffer.EVAL_PACK_ACCESS:
return EvalPackAccess.unmarshal(firstBytes, buffer);
case ITypeMarshalBuffer.EVAL_FOLD_EXPRESSION:
return EvalFoldExpression.unmarshal(firstBytes, buffer);
}
throw new CoreException(CCorePlugin.createStatus("Cannot unmarshal an evaluation, first bytes=" + firstBytes)); //$NON-NLS-1$
}