blob: ce8d9a321412395ddc6f822256231ec79e168e7f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2015 Wind River Systems, Inc. and others.
*
* 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:
* Markus Schorn - initial API and implementation
* Nathan Ridge
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration;
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator.SizeAndAlignment;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
/**
* Arithmetic conversions as required to compute the type of unary or binary expressions.
*/
public abstract class ArithmeticConversion {
private static final int DOMAIN_FLAGS = IBasicType.IS_IMAGINARY | IBasicType.IS_COMPLEX;
private enum Domain {
eReal(0), eImaginary(IBasicType.IS_IMAGINARY), eComplex(IBasicType.IS_COMPLEX);
private final int fModifier;
private Domain(int modifier) {
fModifier = modifier;
}
int getModifier() {
return fModifier;
}
}
private enum Rank {
eInt, eLong, eLongLong
}
protected abstract IBasicType createBasicType(IBasicType.Kind kind, int modifiers);
/**
* Performs an arithmetic conversion as described in section 6.3.1.8 of the C99 standard,
* or 5.0.9 of C++ standard
*/
public final IType convertOperandTypes(int operator, IType op1, IType op2) {
op1 = SemanticUtil.getNestedType(op1, TDEF);
op2 = SemanticUtil.getNestedType(op2, TDEF);
if (!isArithmeticOrUnscopedEnum(op1) || !isArithmeticOrUnscopedEnum(op2)) {
return null;
}
switch (operator) {
// Multiplicative operators
case IASTBinaryExpression.op_divide:
case IASTBinaryExpression.op_modulo:
case IASTBinaryExpression.op_multiply:
// Additive operators
case IASTBinaryExpression.op_minus:
case IASTBinaryExpression.op_plus:
// Bitwise operators
case IASTBinaryExpression.op_binaryAnd:
case IASTBinaryExpression.op_binaryOr:
case IASTBinaryExpression.op_binaryXor:
// Gcc's minimum/maximum operators
case IASTBinaryExpression.op_max:
case IASTBinaryExpression.op_min:
return convert(op1, op2);
case IASTBinaryExpression.op_shiftLeft:
case IASTBinaryExpression.op_shiftRight:
return promote(op1, getDomain(op1));
default:
return null;
}
}
public final IType promoteType(IType type) {
if (!isIntegralOrUnscopedEnum(type))
return null;
return promote(type, getDomain(type));
}
private boolean isArithmeticOrUnscopedEnum(IType op1) {
if (op1 instanceof IBasicType) {
final Kind kind = ((IBasicType) op1).getKind();
switch (kind) {
case eUnspecified:
case eVoid:
case eNullPtr:
return false;
default:
return true;
}
}
if (op1 instanceof IEnumeration) {
if (op1 instanceof ICPPEnumeration && ((ICPPEnumeration) op1).isScoped())
return false;
return true;
}
return false;
}
private boolean isIntegralOrUnscopedEnum(IType op1) {
if (op1 instanceof IEnumeration)
return true;
if (op1 instanceof IBasicType) {
Kind kind = ((IBasicType) op1).getKind();
switch (kind) {
case eBoolean:
case eChar:
case eChar16:
case eChar32:
case eInt:
case eInt128:
case eWChar:
return true;
case eDouble:
case eFloat:
case eFloat128:
case eDecimal32:
case eDecimal64:
case eDecimal128:
case eUnspecified:
case eVoid:
case eNullPtr:
return false;
}
}
return false;
}
private final IType convert(IType type1, IType type2) {
Domain domain = getDomain(type1, type2);
// If either type is a long double, return that type
if (isLongDouble(type1)) {
return adjustDomain((IBasicType) type1, domain);
}
if (isLongDouble(type2)) {
return adjustDomain((IBasicType) type2, domain);
}
// Else if either type is a double return that type
if (isDouble(type1)) {
return adjustDomain((IBasicType) type1, domain);
}
if (isDouble(type2)) {
return adjustDomain((IBasicType) type2, domain);
}
// Else if either type is a float return that type
if (isFloat(type1)) {
return adjustDomain((IBasicType) type1, domain);
}
if (isFloat(type2)) {
return adjustDomain((IBasicType) type2, domain);
}
// We're dealing with integer types so perform integer promotion
IBasicType btype1 = promote(type1, domain);
IBasicType btype2 = promote(type2, domain);
if (btype1.isSameType(btype2)) {
return btype1;
}
if (btype1.isUnsigned() == btype2.isUnsigned()) {
return getIntegerRank(btype1).ordinal() >= getIntegerRank(btype2).ordinal() ? btype1 : btype2;
}
IBasicType unsignedType, signedType;
if (btype1.isUnsigned()) {
unsignedType = btype1;
signedType = btype2;
} else {
unsignedType = btype2;
signedType = btype1;
}
final Rank signedRank = getIntegerRank(signedType);
final Rank unsignedRank = getIntegerRank(unsignedType);
// same rank -> use unsigned
if (unsignedRank.ordinal() >= signedRank.ordinal()) {
return unsignedType;
}
// The signed has the higher rank.
if (signedRank.ordinal() > unsignedRank.ordinal()) {
return signedType;
}
return createBasicType(signedType.getKind(),
changeModifier(signedType.getModifiers(), IBasicType.IS_SIGNED, IBasicType.IS_UNSIGNED));
}
private IBasicType promote(IType type, Domain domain) {
if (type instanceof IEnumeration) {
IType fixedType = null;
if (type instanceof ICPPEnumeration) {
fixedType = ((ICPPEnumeration) type).getFixedType();
}
if (fixedType == null)
return createBasicType(Kind.eInt, domain.getModifier() | getEnumIntTypeModifiers((IEnumeration) type));
type = fixedType;
}
if (type instanceof IBasicType) {
final IBasicType bt = (IBasicType) type;
final Kind kind = bt.getKind();
switch (kind) {
case eBoolean:
case eChar:
case eWChar:
case eChar16:
return createBasicType(Kind.eInt, domain.getModifier());
case eChar32:
// Assuming 32 bits.
return createBasicType(Kind.eInt, domain.getModifier() | IBasicType.IS_UNSIGNED);
case eInt:
if (bt.isShort())
return createBasicType(Kind.eInt, domain.getModifier());
return adjustDomain(bt, domain);
case eInt128:
return createBasicType(Kind.eInt128, domain.getModifier() | IBasicType.IS_UNSIGNED);
case eVoid:
case eUnspecified:
case eDouble:
case eFloat:
case eFloat128:
case eDecimal32:
case eDecimal64:
case eDecimal128:
case eNullPtr:
assert false;
}
}
return createBasicType(Kind.eInt, domain.getModifier());
}
private Domain getDomain(IType type1, IType type2) {
Domain d1 = getDomain(type1);
Domain d2 = getDomain(type2);
if (d1 == d2)
return d1;
return Domain.eComplex;
}
private Domain getDomain(IType type) {
if (type instanceof IBasicType) {
IBasicType bt = (IBasicType) type;
if (bt.isComplex())
return Domain.eComplex;
if (bt.isImaginary())
return Domain.eImaginary;
}
return Domain.eReal;
}
private IBasicType adjustDomain(IBasicType t, Domain d) {
Domain myDomain = getDomain(t);
if (myDomain == d)
return t;
return createBasicType(t.getKind(), changeModifier(t.getModifiers(), DOMAIN_FLAGS, d.getModifier()));
}
private int changeModifier(int modifiers, int remove, int add) {
return (modifiers & ~remove) | add;
}
private Rank getIntegerRank(IBasicType type) {
if (type.getKind() == Kind.eInt128)
return Rank.eLongLong;
assert type.getKind() == Kind.eInt;
if (type.isLongLong())
return Rank.eLongLong;
if (type.isLong())
return Rank.eLong;
return Rank.eInt;
}
private boolean isLongDouble(IType type) {
if (type instanceof IBasicType) {
final IBasicType bt = (IBasicType) type;
return bt.isLong() && bt.getKind() == Kind.eDouble || bt.getKind() == Kind.eFloat128
|| bt.getKind() == Kind.eDecimal128;
}
return false;
}
private static boolean isDouble(IType type) {
if (type instanceof IBasicType) {
final IBasicType bt = (IBasicType) type;
return bt.getKind() == Kind.eDouble || bt.getKind() == Kind.eDecimal64;
}
return false;
}
private static boolean isFloat(IType type) {
if (type instanceof IBasicType) {
final IBasicType bt = (IBasicType) type;
return bt.getKind() == Kind.eFloat || bt.getKind() == Kind.eDecimal32;
}
return false;
}
public static int getEnumIntTypeModifiers(IEnumeration enumeration) {
final long minValue = enumeration.getMinValue();
final long maxValue = enumeration.getMaxValue();
// TODO(sprigogin): Use values of __INT_MAX__ and __LONG_MAX__ macros
if (minValue >= Integer.MIN_VALUE && maxValue <= Integer.MAX_VALUE) {
return 0;
} else if (minValue >= 0 && maxValue <= 0xFFFFFFFFL) {
return IBasicType.IS_UNSIGNED;
} else if (minValue >= Long.MIN_VALUE && maxValue <= Long.MAX_VALUE) {
return IBasicType.IS_LONG;
} else {
// This branch is unreachable due to limitations of Java long type.
return IBasicType.IS_UNSIGNED | IBasicType.IS_LONG;
}
}
public static boolean fitsIntoType(IBasicType basicTarget, long n) {
final Kind kind = basicTarget.getKind();
switch (kind) {
case eBoolean:
return n == 0 || n == 1;
case eInt:
if (!basicTarget.isUnsigned()) {
if (basicTarget.isShort()) {
return Short.MIN_VALUE <= n && n <= Short.MAX_VALUE;
}
// Can't represent long longs with Java longs.
if (basicTarget.isLong() || basicTarget.isLongLong()) {
return true;
}
return Integer.MIN_VALUE <= n && n <= Integer.MAX_VALUE;
}
if (n < 0)
return false;
if (basicTarget.isShort()) {
return n < (Short.MAX_VALUE + 1L) * 2;
}
// Can't represent long longs with Java longs.
if (basicTarget.isLong() || basicTarget.isLongLong()) {
return true;
}
return n < (Integer.MAX_VALUE + 1L) * 2;
case eChar:
return 0 <= n && n <= 0xFF;
case eChar16:
case eWChar:
return 0 <= n && n <= 0xFFFF;
case eChar32:
return 0 <= n && n <= 0xFFFFFFFFL;
case eDecimal32:
return Integer.MIN_VALUE <= n && n <= Integer.MAX_VALUE;
case eFloat:
float f = n;
return (long) f == n;
case eDouble:
double d = n;
return (long) d == n;
default:
return false;
}
}
/**
* Makes a best-effort guess at the sizeof() of an integral type.
*/
private static long getApproximateSize(IBasicType type) {
switch (type.getKind()) {
case eChar:
return 1;
case eWChar:
return 2;
case eInt:
// Note: we return 6 for long so that both long -> int
// and long long -> long conversions are reported
// as narrowing, to be on the safe side.
return type.isShort() ? 2 : type.isLong() ? 6 : type.isLongLong() ? 8 : 4;
case eBoolean:
return 1;
case eChar16:
return 2;
case eChar32:
return 4;
case eInt128:
return 16;
default:
return 0; // shouldn't happen
}
}
/**
* Checks whether a target integral type can represent all values of a source integral type.
*
* @param target the target integral type
* @param source the source integral type
* @return whether the target integral type can represent all values of the source integral type
*/
public static boolean fitsIntoType(IBasicType target, IBasicType source) {
// A boolean cannot represent any other type.
if (target.getKind() == Kind.eBoolean && source.getKind() != Kind.eBoolean)
return false;
// A boolean can be represented by any other integral type.
if (source.getKind() == Kind.eBoolean)
return true;
// If the source is signed, it might be negative, so an unsigned target cannot represent it.
if (!source.isUnsigned() && target.isUnsigned())
return false;
// Otherwise, go by the size and signedness of the type.
SizeAndAlignment sourceSizeAndAlignment = SizeofCalculator.getSizeAndAlignment(source);
SizeAndAlignment targetSizeAndAlignment = SizeofCalculator.getSizeAndAlignment(target);
long sizeofSource = sourceSizeAndAlignment == null ? getApproximateSize(source) : sourceSizeAndAlignment.size;
long sizeofTarget = targetSizeAndAlignment == null ? getApproximateSize(target) : targetSizeAndAlignment.size;
if (sizeofSource == sizeofTarget) {
return target.isUnsigned() == source.isUnsigned();
} else {
return sizeofSource < sizeofTarget;
}
}
}