1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 06:32:10 +02:00

Bug 506972 - Contextual conversion to bool for &&, || and !

Suggestion for implementation of contextual conversion of explicit
conversion operator.
+ Test

Change-Id: I69537f86a9b09120e31115fbe94391a90196952a
Signed-off-by: Thomas Corbat <tcorbat@hsr.ch>
This commit is contained in:
Thomas Corbat 2016-12-14 11:42:27 +01:00 committed by Gerrit Code Review @ Eclipse.org
parent eaa1442345
commit 2ee0a093d0
11 changed files with 115 additions and 17 deletions

View file

@ -94,6 +94,7 @@ import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCastExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier;
@ -113,6 +114,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTPointerToMember;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTWhileStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
@ -12174,4 +12176,40 @@ public class AST2CPPTests extends AST2TestBase {
BindingAssertionHelper helper = getAssertionHelper();
helper.assertVariableValue("waldo", 8);
}
// struct BooleanConvertible {
// constexpr explicit operator bool() const { return true; }
// };
// void explicitBooleanContexts() {
// BooleanConvertible bc{};
// bc && bc;
// bc || bc;
// !bc;
// }
public void testContextualBooleanConversion_506972() throws Exception {
IASTTranslationUnit tu = parseAndCheckBindings();
IASTDeclaration explicitBooleanContextsFunction = tu.getDeclarations()[1];
IASTNode functionBody = explicitBooleanContextsFunction.getChildren()[2];
IASTNode logicalAndExpressionStatement = functionBody.getChildren()[1];
ICPPASTBinaryExpression logicalAndExpression = (ICPPASTBinaryExpression)logicalAndExpressionStatement.getChildren()[0];
ICPPFunction logicalAndOverload = logicalAndExpression.getOverload();
assertNotNull(logicalAndOverload);
ICPPFunctionType logicalAndType = logicalAndOverload.getType();
isTypeEqual(logicalAndType, "bool (bool, bool)");
IASTNode logicalOrExpressionStatement = functionBody.getChildren()[2];
ICPPASTBinaryExpression logicalOrExpression = (ICPPASTBinaryExpression)logicalOrExpressionStatement.getChildren()[0];
ICPPFunction logicalOrOverload = logicalOrExpression.getOverload();
assertNotNull(logicalOrOverload);
ICPPFunctionType logicalOrType = logicalOrOverload.getType();
isTypeEqual(logicalOrType, "bool (bool, bool)");
IASTNode logicalNotExpressionStatement = functionBody.getChildren()[3];
ICPPASTUnaryExpression logicalNotExpression = (ICPPASTUnaryExpression)logicalNotExpressionStatement.getChildren()[0];
ICPPFunction logicalNotOverload = logicalNotExpression.getOverload();
assertNotNull(logicalNotOverload);
ICPPFunctionType logicalNotType = logicalNotOverload.getType();
isTypeEqual(logicalNotType, "bool (bool)");
}
}

View file

@ -52,4 +52,30 @@ public class BinaryExpressionTests extends TestBase {
public void testBinaryExpressionSequence() throws Exception {
assertEvaluationEquals(12);
}
// struct BooleanConvertible {
// bool value;
// constexpr explicit operator bool() const {
// return value;
// }
// };
// constexpr BooleanConvertible variable{true};
// constexpr bool actual = variable && variable;
public void testContextualConversionInAnd_506972() throws Exception {
assertEvaluationEquals(true);
}
// struct BooleanConvertible {
// bool value;
// constexpr explicit operator bool() const {
// return value;
// }
// };
// constexpr BooleanConvertible variable{true};
// constexpr bool actual = variable || variable;
public void testContextualConversionInOr_506972() throws Exception {
assertEvaluationEquals(true);
}
}

View file

@ -130,4 +130,17 @@ public class UnaryExpressionTests extends TestBase {
public void testPrefixIncrementReturnsLvalue() throws Exception {
assertEvaluationEquals(4);
}
// struct BooleanConvertible {
// bool value;
// constexpr explicit operator bool() const {
// return value;
// }
// };
// constexpr BooleanConvertible variable{true};
// constexpr bool actual = !variable;
public void testContextualConversionInNot_506972() throws Exception {
assertEvaluationEquals(false);
}
}

View file

@ -11,6 +11,10 @@
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.CVTYPE;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.REF;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory;
@ -148,16 +152,18 @@ public abstract class CPPEvaluation implements ICPPEvaluation {
* @param argument the evaluation to convert
* @param targetType the type to convert to
* @param point point of instantiation for name lookups
* @param allowContextualConversion enable/disable explicit contextual conversion
*/
protected static ICPPEvaluation maybeApplyConversion(ICPPEvaluation argument, IType targetType,
IASTNode point) {
IASTNode point, boolean allowContextualConversion) {
IType type = argument.getType(point);
IType uqType= SemanticUtil.getNestedType(type, TDEF | REF | CVTYPE);
ValueCategory valueCategory = argument.getValueCategory(point);
ICPPFunction conversion = null;
if (type instanceof ICPPClassType) {
if (uqType instanceof ICPPClassType) {
try {
Cost cost = Conversions.initializationByConversion(valueCategory, type, (ICPPClassType) type,
targetType, false, point);
Cost cost = Conversions.initializationByConversion(valueCategory, type, (ICPPClassType) uqType,
targetType, false, point, allowContextualConversion);
conversion = cost.getUserDefinedConversion();
} catch (DOMException e) {
CCorePlugin.log(e);
@ -167,8 +173,8 @@ public abstract class CPPEvaluation implements ICPPEvaluation {
if (!conversion.isConstexpr()) {
return EvalFixed.INCOMPLETE;
}
ICPPEvaluation eval = new EvalBinding(conversion, null, (IBinding) null);
argument = new EvalFunctionCall(new ICPPEvaluation[] {eval, argument}, null, (IBinding) null);
ICPPEvaluation eval = new EvalMemberAccess(uqType, valueCategory, conversion, argument, false, point);
argument = new EvalFunctionCall(new ICPPEvaluation[] { eval }, null, (IBinding) null);
}
return argument;
}

View file

@ -2804,7 +2804,7 @@ public class CPPTemplates {
Context.ORDINARY, point);
if (cost == null || !cost.converts()) {
ICPPEvaluation eval = arg.getNonTypeEvaluation();
ICPPEvaluation newEval = CPPEvaluation.maybeApplyConversion(eval, p, point);
ICPPEvaluation newEval = CPPEvaluation.maybeApplyConversion(eval, p, point, false);
if (newEval == EvalFixed.INCOMPLETE && newEval != eval)
return null;
return new CPPTemplateNonTypeArgument(newEval, point);

View file

@ -323,7 +323,7 @@ public class Conversions {
if (udc == UDCMode.FORBIDDEN)
return Cost.NO_CONVERSION;
return initializationByConversion(valueCat, source, (ICPPClassType) uqSource, target, udc == UDCMode.DEFER, point);
return initializationByConversion(valueCat, source, (ICPPClassType) uqSource, target, udc == UDCMode.DEFER, point, false);
}
return checkStandardConversionSequence(uqSource, target, point);
@ -839,7 +839,7 @@ public class Conversions {
/**
* 13.3.1.5 Initialization by conversion function [over.match.conv]
*/
static Cost initializationByConversion(ValueCategory valueCat, IType source, ICPPClassType uqSource, IType target, boolean deferUDC, IASTNode point) throws DOMException {
static Cost initializationByConversion(ValueCategory valueCat, IType source, ICPPClassType uqSource, IType target, boolean deferUDC, IASTNode point, boolean allowExplicitConversion) throws DOMException {
if (deferUDC) {
Cost c= new Cost(source, target, Rank.USER_DEFINED_CONVERSION);
c.setDeferredUDC(DeferredUDC.INIT_BY_CONVERSION);
@ -852,7 +852,7 @@ public class Conversions {
for (final ICPPFunction f : ops) {
if (f instanceof ICPPMethod && !(f instanceof IProblemBinding)) {
ICPPMethod op= (ICPPMethod) f;
final boolean isExplicitConversion= op.isExplicit();
final boolean isExplicitConversion= op.isExplicit() && !allowExplicitConversion;
if (isExplicitConversion /** && !direct **/)
continue;

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2014 Wind River Systems, Inc. and others.
* Copyright (c) 2012, 2017 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -166,6 +166,10 @@ public class EvalBinary extends CPPDependentEvaluation {
return operatorCall;
}
private boolean operatorAllowsContextualConversion() {
return fOperator == op_logicalAnd || fOperator == op_logicalOr;
}
@Override
public IValue getValue(IASTNode point) {
ICPPEvaluation arg1 = fArg1;
@ -174,8 +178,9 @@ public class EvalBinary extends CPPDependentEvaluation {
if (overload != null) {
IType[] parameterTypes = SemanticUtil.getParameterTypesIncludingImplicitThis(overload);
if (parameterTypes.length >= 2) {
arg1 = maybeApplyConversion(fArg1, parameterTypes[0], point);
arg2 = maybeApplyConversion(fArg2, parameterTypes[1], point);
boolean allowContextualConversion = operatorAllowsContextualConversion();
arg1 = maybeApplyConversion(fArg1, parameterTypes[0], point, allowContextualConversion);
arg2 = maybeApplyConversion(fArg2, parameterTypes[1], point, allowContextualConversion);
} else {
CCorePlugin.log(IStatus.ERROR, "Unexpected overload for binary operator " + fOperator //$NON-NLS-1$
+ ": '" + overload.getName() + "'"); //$NON-NLS-1$//$NON-NLS-2$

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2016 Wind River Systems, Inc. and others.
* Copyright (c) 2012, 2014 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at

View file

@ -390,7 +390,7 @@ public final class EvalFunctionCall extends CPPDependentEvaluation {
break;
} else {
if (j < arguments.length) {
ICPPEvaluation argument = maybeApplyConversion(arguments[j++], param.getType(), point);
ICPPEvaluation argument = maybeApplyConversion(arguments[j++], param.getType(), point, false);
record.update(param, argument);
} else if (param.hasDefaultValue()) {
IValue value = param.getDefaultValue();

View file

@ -300,7 +300,7 @@ public class EvalUnary extends CPPDependentEvaluation {
if (parameterTypes.length == 0)
return IntegralValue.ERROR;
IType targetType = parameterTypes[0];
arg = maybeApplyConversion(arg, targetType, point);
arg = maybeApplyConversion(arg, targetType, point, fOperator == op_not);
if (!(overload instanceof CPPImplicitFunction)) {
if (!overload.isConstexpr())

View file

@ -17,6 +17,8 @@ import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUti
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType;
import java.util.Arrays;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory;
import org.eclipse.cdt.core.dom.ast.IASTNode;
@ -30,6 +32,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTranslationUnit;
import org.eclipse.cdt.internal.core.dom.parser.cpp.OverloadableOperator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPTemplates.TypeSelection;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost.DeferredUDC;
@ -106,7 +109,7 @@ class FunctionCost {
case INIT_BY_CONVERSION:
IType uqSource= getNestedType(cost.source, TDEF | REF | CVTYPE);
udcCost = Conversions.initializationByConversion(fValueCategories[i], cost.source,
(ICPPClassType) uqSource, cost.target, false, point);
(ICPPClassType) uqSource, cost.target, false, point, allowsContextualBooleanConversion());
break;
case LIST_INIT_OF_CLASS:
udcCost = Conversions.listInitializationOfClass(((InitializerListType) cost.source).getEvaluation(),
@ -128,6 +131,13 @@ class FunctionCost {
return true;
}
private boolean allowsContextualBooleanConversion() {
char[] functionName = fFunction.getNameCharArray();
return Arrays.equals(functionName, OverloadableOperator.AND.toCharArray()) ||
Arrays.equals(functionName, OverloadableOperator.OR.toCharArray()) ||
Arrays.equals(functionName, OverloadableOperator.NOT.toCharArray());
}
/**
* Compares this function call cost to another one.
*/