diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java index ee0d36b4f8b..755bf7bafea 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java @@ -113,6 +113,7 @@ public class AST2TestBase extends SemanticTestBase { map.put("__SIZEOF_SHORT__", "2"); map.put("__SIZEOF_INT__", "4"); map.put("__SIZEOF_LONG__", "8"); + map.put("__SIZEOF_LONG_LONG__", "8"); map.put("__SIZEOF_DOUBLE__", "8"); map.put("__SIZEOF_POINTER__", "8"); return map; @@ -123,6 +124,7 @@ public class AST2TestBase extends SemanticTestBase { map.put("__SIZEOF_SHORT__", "2"); map.put("__SIZEOF_INT__", "4"); map.put("__SIZEOF_LONG__", "8"); + map.put("__SIZEOF_LONG_LONG__", "8"); map.put("__SIZEOF_DOUBLE__", "8"); map.put("__SIZEOF_POINTER__", "8"); return map; diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/IntegralValueTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/IntegralValueTests.java index 7b0d1f26ce3..226ac681328 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/IntegralValueTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/IntegralValueTests.java @@ -11,6 +11,7 @@ *******************************************************************************/ package org.eclipse.cdt.core.parser.tests.ast2.cxx14.constexpr; +import org.eclipse.cdt.core.testplugin.TestScannerProvider; import org.eclipse.cdt.internal.core.dom.parser.IntegralValue; import junit.framework.TestSuite; @@ -36,6 +37,16 @@ public abstract class IntegralValueTests extends TestBase { } } + @Override + protected void setUp() throws Exception { + TestScannerProvider.sDefinedSymbols.put("__SIZEOF_SHORT__", "2"); + TestScannerProvider.sDefinedSymbols.put("__SIZEOF_INT__", "4"); + TestScannerProvider.sDefinedSymbols.put("__SIZEOF_LONG__", "8"); + TestScannerProvider.sDefinedSymbols.put("__SIZEOF_LONG_LONG__", "8"); + TestScannerProvider.sDefinedSymbols.put("__SIZEOF_POINTER__", "8"); + super.setUp(); + } + // constexpr auto x = int{} + int(); public void testIntDefaultValue() throws Exception { assertEvaluationEquals(0); @@ -275,6 +286,16 @@ public abstract class IntegralValueTests extends TestBase { assertEvaluationEquals(5); } + // constexpr int x = __builtin_ffs(0x100000000); + public void testBuiltinFfsNarrowing() throws Exception { + assertEvaluationEquals(0); + } + + // constexpr int x = __builtin_ffsl(0x100000000); + public void testBuiltinFfsl() throws Exception { + assertEvaluationEquals(33); + } + // constexpr int x = __builtin_ctz(16); public void testBuiltinCtz() throws Exception { assertEvaluationEquals(4); @@ -314,4 +335,9 @@ public abstract class IntegralValueTests extends TestBase { public void testBuiltinAbsNegativeInput() throws Exception { assertEvaluationEquals(1); } + + // constexpr int x = __builtin_abs(0xFFFFFFFF); + public void testBuiltinAbsNarrowing() throws Exception { + assertEvaluationEquals(1); + } } \ No newline at end of file diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java index 79badd6a337..81ef2344c50 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java @@ -66,6 +66,7 @@ public abstract class TestBase extends IndexBindingResolutionTestBase { map.put("__SIZEOF_SHORT__", "2"); map.put("__SIZEOF_INT__", "4"); map.put("__SIZEOF_LONG__", "8"); + map.put("__SIZEOF_LONG_LONG__", "8"); map.put("__SIZEOF_POINTER__", "8"); return map; } 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 57c315ef531..fb3d3e4157f 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 @@ -30,6 +30,7 @@ import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUti import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.isVoidType; +import java.math.BigInteger; import java.util.Collections; import org.eclipse.cdt.core.dom.ast.DOMException; @@ -60,6 +61,8 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.ArithmeticConversion; import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer; +import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator; +import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator.SizeAndAlignment; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerToMemberType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType; @@ -1326,4 +1329,99 @@ public class Conversions { private static boolean isNullPtr(IType t1) { return t1 instanceof IBasicType && ((IBasicType) t1).getKind() == Kind.eNullPtr; } + + /** + * Narrow a numeric value to the range of a specified type. + * @param num the value to narrow (may be null) + * @param toType the type to narrow to + * @return a number representing the narrowed value, or null + */ + public static Number narrowNumberValue(Number num, IType toType) { + if (num == null) + return null; + + if (toType instanceof IBasicType) { + IBasicType basicType = (IBasicType) toType; + IBasicType.Kind basicTypeKind = basicType.getKind(); + switch (basicTypeKind) { + case eFloat: + if (num instanceof Float) + return num; + return Float.valueOf(num.floatValue()); + case eDouble: + if (num instanceof Double) + return num; + return Double.valueOf(num.doubleValue()); + case eInt: + SizeAndAlignment sizeToType = SizeofCalculator.getSizeAndAlignment(toType); + if (sizeToType == null) + return null; + // Note in the following we don't check type.isSigned() since that checks for the + // explicit presence of the "signed" modifier. So instead check !type.isUnsigned(). + if (sizeToType.size <= 8) { + // First, mask the value to the correct size + // Note that we take the longValue here which may be negative even though the + // original value is positive; the masking here should still be correct and we + // should ultimately end up with the correct narrowed value, regardless. + long longVal = num.longValue(); + long maskVal = 0xFFFFFFFFFFFFFFFFL; + long signBit = 0x8000000000000000L; + // Calculate a mask to reduce the size of the value to the target width: + maskVal >>>= (8 - sizeToType.size) * 8; + signBit >>>= (8 - sizeToType.size) * 8; + if (!basicType.isUnsigned() && (longVal & signBit) != 0) { + // We need to extend the sign bit. + long signBits = ~maskVal; + longVal |= signBits; + } else { + longVal &= maskVal; + } + + // The Java type used to store the numerical value is independent of the associated + // C type, but we go with a smaller type (Integer) where possible. For 4 bytes + // (signed) or less than 4 bytes (signed or not) we can use Integer. For 8 bytes + // (signed) or less than 8 bytes (signed or not) we can use Long. Any larger and we + // resort to BigInteger. + if (longVal >= 0 && longVal <= Integer.MAX_VALUE) { + return Integer.valueOf((int) longVal); + } + if (!basicType.isUnsigned() && longVal >= Integer.MIN_VALUE && longVal <= Integer.MAX_VALUE) { + return Integer.valueOf((int) longVal); + } + + if (!basicType.isUnsigned() || longVal > 0) { + return Long.valueOf(longVal); + } + + BigInteger biVal = BigInteger.valueOf(longVal); + // 2**64 = 18446744073709551616 + biVal = biVal.add(new BigInteger("18446744073709551616")); //$NON-NLS-1$ + return biVal; + } + // TODO handle larger int sizes? + return null; + case eChar: + // TODO don't assume signed char + if (num instanceof Byte) + return num; + return Byte.valueOf(num.byteValue()); + case eChar16: + int intVal = num.intValue(); + int maskedVal = intVal & 0xFFFF; + if (maskedVal == intVal && num instanceof Integer) + return num; + return Integer.valueOf(maskedVal); + case eChar32: + long longVal = num.longValue(); + long maskedVal32 = longVal & 0xFFFFFFFFL; + if (maskedVal32 == longVal && (num instanceof Integer || num instanceof Long)) + return num; + return Long.valueOf(maskedVal32); + default: + return null; + } + } + + return null; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java index 3e2df54fa63..86e6aab7ae2 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java @@ -55,25 +55,35 @@ public class ExecBuiltin implements ICPPExecution { switch (funcId) { case BUILTIN_FFS: + return executeBuiltinFfs(record, context, intType); case BUILTIN_FFSL: + return executeBuiltinFfs(record, context, longType); case BUILTIN_FFSLL: - return executeBuiltinFfs(record, context); + return executeBuiltinFfs(record, context, longlongType); case BUILTIN_CTZ: + return executeBuiltinCtz(record, context, intType); case BUILTIN_CTZL: + return executeBuiltinCtz(record, context, longType); case BUILTIN_CTZLL: - return executeBuiltinCtz(record, context); + return executeBuiltinCtz(record, context, longlongType); case BUILTIN_POPCOUNT: + return executeBuiltinPopcount(record, context, intType); case BUILTIN_POPCOUNTL: + return executeBuiltinPopcount(record, context, longType); case BUILTIN_POPCOUNTLL: - return executeBuiltinPopcount(record, context); + return executeBuiltinPopcount(record, context, longlongType); case BUILTIN_PARITY: + return executeBuiltinParity(record, context, intType); case BUILTIN_PARITYL: + return executeBuiltinParity(record, context, longType); case BUILTIN_PARITYLL: - return executeBuiltinParity(record, context); + return executeBuiltinParity(record, context, longlongType); case BUILTIN_ABS: + return executeBuiltinAbs(record, context, intType); case BUILTIN_LABS: + return executeBuiltinAbs(record, context, longType); case BUILTIN_LLABS: - return executeBuiltinAbs(record, context); + return executeBuiltinAbs(record, context, longlongType); } return null; } @@ -82,16 +92,18 @@ public class ExecBuiltin implements ICPPExecution { * Return an execution representing __builtin_ffs or __builtin_ctz */ private ICPPExecution executeBuiltinFfsCtz(ActivationRecord record, ConstexprEvaluationContext context, - boolean isCtz) { + boolean isCtz, IType argType) { ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); IValue argValue = arg0.getValue(); - if (!(argValue instanceof IntegralValue)) + Number numberVal = argValue.numberValue(); + numberVal = Conversions.narrowNumberValue(numberVal, argType); + if (numberVal == null) return null; // __builtin_ffs returns 0 if arg is 0, or 1+count where count is the number of trailing 0 bits // __builtin_ctz is undefined if arg is 0, or returns count - long arg = argValue.numberValue().longValue(); + long arg = numberVal.longValue(); if (arg == 0) { if (isCtz) { return null; @@ -108,26 +120,30 @@ public class ExecBuiltin implements ICPPExecution { return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(count + increment))); } - private ICPPExecution executeBuiltinFfs(ActivationRecord record, ConstexprEvaluationContext context) { - return executeBuiltinFfsCtz(record, context, false /* ffs */); + private ICPPExecution executeBuiltinFfs(ActivationRecord record, ConstexprEvaluationContext context, + IType argType) { + return executeBuiltinFfsCtz(record, context, false /* ffs */, argType); } - private ICPPExecution executeBuiltinCtz(ActivationRecord record, ConstexprEvaluationContext context) { - return executeBuiltinFfsCtz(record, context, true /* ctz */); + private ICPPExecution executeBuiltinCtz(ActivationRecord record, ConstexprEvaluationContext context, + IType argType) { + return executeBuiltinFfsCtz(record, context, true /* ctz */, argType); } /* * Return an execution representing __builtin_popcount */ private ICPPExecution executeBuiltinPopcountParity(ActivationRecord record, ConstexprEvaluationContext context, - boolean isParity) { + boolean isParity, IType argType) { ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); IValue argValue = arg0.getValue(); - if (!(argValue instanceof IntegralValue)) + Number numberVal = argValue.numberValue(); + numberVal = Conversions.narrowNumberValue(numberVal, argType); + if (numberVal == null) return null; - long arg = argValue.numberValue().longValue(); + long arg = numberVal.longValue(); int count = 0; while (arg != 0) { if ((arg & 1) != 0) @@ -140,38 +156,30 @@ public class ExecBuiltin implements ICPPExecution { return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(count))); } - private ICPPExecution executeBuiltinPopcount(ActivationRecord record, ConstexprEvaluationContext context) { - return executeBuiltinPopcountParity(record, context, false); + private ICPPExecution executeBuiltinPopcount(ActivationRecord record, ConstexprEvaluationContext context, + IType argType) { + return executeBuiltinPopcountParity(record, context, false, argType); } - private ICPPExecution executeBuiltinParity(ActivationRecord record, ConstexprEvaluationContext context) { - return executeBuiltinPopcountParity(record, context, true); + private ICPPExecution executeBuiltinParity(ActivationRecord record, ConstexprEvaluationContext context, + IType argType) { + return executeBuiltinPopcountParity(record, context, true, argType); } - private ICPPExecution executeBuiltinAbs(ActivationRecord record, ConstexprEvaluationContext context) { + private ICPPExecution executeBuiltinAbs(ActivationRecord record, ConstexprEvaluationContext context, + IType argType) { ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); IValue argValue = arg0.getValue(); - if (!(argValue instanceof IntegralValue)) + Number argNumber = argValue.numberValue(); + argNumber = Conversions.narrowNumberValue(argNumber, argType); + if (argNumber == null) return null; - long arg = argValue.numberValue().longValue(); + long arg = argNumber.longValue(); long result = Math.abs(arg); - IType resultType = null; - switch (funcId) { - case BUILTIN_ABS: - resultType = intType; - break; - case BUILTIN_LABS: - resultType = longType; - break; - case BUILTIN_LLABS: - resultType = longlongType; - break; - } - - return new ExecReturn(new EvalFixed(resultType, ValueCategory.PRVALUE, IntegralValue.create(result))); + return new ExecReturn(new EvalFixed(argType, ValueCategory.PRVALUE, IntegralValue.create(result))); } @Override