From 2ee0a093d0b03abf8c116c3b18090fd787dd5cd6 Mon Sep 17 00:00:00 2001 From: Thomas Corbat Date: Wed, 14 Dec 2016 11:42:27 +0100 Subject: [PATCH] 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 --- .../core/parser/tests/ast2/AST2CPPTests.java | 38 +++++++++++++++++++ .../BinaryExpressionTests.java | 26 +++++++++++++ .../UnaryExpressionTests.java | 13 +++++++ .../parser/cpp/semantics/CPPEvaluation.java | 18 ++++++--- .../parser/cpp/semantics/CPPTemplates.java | 2 +- .../dom/parser/cpp/semantics/Conversions.java | 6 +-- .../dom/parser/cpp/semantics/EvalBinary.java | 11 ++++-- .../dom/parser/cpp/semantics/EvalBinding.java | 2 +- .../cpp/semantics/EvalFunctionCall.java | 2 +- .../dom/parser/cpp/semantics/EvalUnary.java | 2 +- .../parser/cpp/semantics/FunctionCost.java | 12 +++++- 11 files changed, 115 insertions(+), 17 deletions(-) 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 728178e18ef..a0fadad9363 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 @@ -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)"); + } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/constexprevaluation/BinaryExpressionTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/constexprevaluation/BinaryExpressionTests.java index d8276d0dc3a..ebe56ff9b63 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/constexprevaluation/BinaryExpressionTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/constexprevaluation/BinaryExpressionTests.java @@ -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); + } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/constexprevaluation/UnaryExpressionTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/constexprevaluation/UnaryExpressionTests.java index 8dbbe057c93..9b05af0a53c 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/constexprevaluation/UnaryExpressionTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/constexprevaluation/UnaryExpressionTests.java @@ -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); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPEvaluation.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPEvaluation.java index deee3912337..45131e94eb1 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPEvaluation.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPEvaluation.java @@ -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; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPTemplates.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPTemplates.java index 2039ab70393..65739ddeea4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPTemplates.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPTemplates.java @@ -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); 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 8379a771290..fa543be098e 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 @@ -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; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalBinary.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalBinary.java index 455fc6ac8d4..584973564f3 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalBinary.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalBinary.java @@ -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$ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalBinding.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalBinding.java index 98ca0ea229d..53b34f23edc 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalBinding.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalBinding.java @@ -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 diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalFunctionCall.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalFunctionCall.java index 276faf3a1db..4248ec751d4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalFunctionCall.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalFunctionCall.java @@ -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(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalUnary.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalUnary.java index a76fef3faa6..4919a2bbe1d 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalUnary.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalUnary.java @@ -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()) diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/FunctionCost.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/FunctionCost.java index 6dfee36aef6..6f116871bfd 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/FunctionCost.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/FunctionCost.java @@ -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. */