/*******************************************************************************
* Copyright (c) 2012, 2016 Google, 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.ARRAY;
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.TDEF;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBasicType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPField;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMember;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUnaryTypeTransformation.Operator;
import org.eclipse.cdt.core.dom.ast.cpp.SemanticQueries;
import org.eclipse.cdt.internal.core.dom.parser.ArithmeticConversion;
import org.eclipse.cdt.internal.core.dom.parser.ProblemType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFunction;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitConstructor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPUnaryTypeTransformation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper.MethodKind;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalFunction;
/**
* A collection of static methods for determining type traits.
*/
public class TypeTraits {
private static final ICPPBasicType[] SIGNED_UNDERLYING_ENUM_TYPES = {
CPPBasicType.INT,
CPPBasicType.LONG,
CPPBasicType.LONG_LONG,
CPPBasicType.INT128
};
private static final ICPPBasicType[] UNSIGNED_UNDERLYING_ENUM_TYPES = {
CPPBasicType.UNSIGNED_INT,
CPPBasicType.UNSIGNED_LONG,
CPPBasicType.UNSIGNED_LONG_LONG,
CPPBasicType.UNSIGNED_INT128
};
private TypeTraits() {}
public static boolean isDefaultedMethod(ICPPMethod method) {
if (method instanceof ICPPInternalFunction) {
ICPPInternalFunction internalFunc = (ICPPInternalFunction) method;
IASTNode definition = internalFunc.getDefinition();
ICPPASTFunctionDefinition functionDefinition = CPPFunction.getFunctionDefinition(definition);
if (functionDefinition != null) {
return functionDefinition.isDefaulted();
}
}
return false;
}
/**
* From $3.9 / 10:
* A type is a literal type if it is:
* [...]
* - a possibly cv-qualified class type that has all the following properties:
* - it has a trivial destructor
* - it is an aggregate type or has at least one constexpr constructor or constructor template that is not a
* copy or move constructor, and
* - all of its non-static data members and base classes are of non-volatile literal types
* TODO: The last property isn't being checked.
*/
public static boolean isLiteralClass(ICPPClassType classType, IASTNode point) {
if (!hasTrivialDestructor(classType, point)) {
return false;
}
if (isAggregateClass(classType, point)) {
return true;
}
ICPPConstructor[] ctors = ClassTypeHelper.getConstructors(classType, point);
for (ICPPConstructor ctor : ctors) {
MethodKind methodKind = ClassTypeHelper.getMethodKind(classType, ctor);
if (methodKind == MethodKind.COPY_CTOR || methodKind == MethodKind.MOVE_CTOR) {
continue;
}
// implicit constructors are automatically constexpr when the class is a literal type
if (ctor instanceof CPPImplicitConstructor || ctor.isConstexpr()) {
return true;
}
}
return false;
}
/**
* C++11: 9-6
*/
public static boolean isTrivial(ICPPClassType classType, IASTNode point) {
return isTrivialImpl(classType, point, true);
}
private static boolean isTrivialImpl(ICPPClassType classType, IASTNode point,
boolean checkDefaultConstructors) {
for (ICPPMethod method : ClassTypeHelper.getDeclaredMethods(classType, point)) {
if (method.isVirtual())
return false;
switch (ClassTypeHelper.getMethodKind(classType, method)) {
case DEFAULT_CTOR:
if (checkDefaultConstructors) {
return false;
}
break;
case COPY_CTOR:
case MOVE_CTOR:
case COPY_ASSIGNMENT_OP:
case MOVE_ASSIGNMENT_OP:
case DTOR:
return false;
default:
break;
}
}
ICPPField[] fields = ClassTypeHelper.getDeclaredFields(classType, point);
for (ICPPField field : fields) {
if (!field.isStatic()) {
IType fieldType = SemanticUtil.getNestedType(field.getType(), TDEF);
if (fieldType instanceof ICPPClassType && !isTrivial((ICPPClassType) fieldType, point))
return false;
}
}
for (ICPPBase base : ClassTypeHelper.getBases(classType, point)) {
if (base.isVirtual())
return false;
}
ICPPClassType[] baseClasses = ClassTypeHelper.getAllBases(classType, point);
for (ICPPClassType baseClass : baseClasses) {
if (!isTrivial(baseClass, point))
return false;
}
return true;
}
/**
* C++11: 9-7
*/
public static boolean isStandardLayout(IType type, IASTNode point) {
type = SemanticUtil.getNestedType(type, ARRAY | CVTYPE | TDEF);
if (type instanceof ICPPReferenceType)
return false;
if (!(type instanceof ICPPClassType))
return true;
ICPPClassType classType = (ICPPClassType) type;
int visibility = 0;
ICPPField firstNonStaticField = null;
ICPPField[] fields = ClassTypeHelper.getDeclaredFields(classType, point);
for (ICPPField field : fields) {
if (!field.isStatic()) {
if (!isStandardLayout(field.getType(), point))
return false;
int vis = field.getVisibility();
if (visibility == 0) {
visibility = vis;
} else if (vis != visibility) {
return false;
}
if (firstNonStaticField == null)
firstNonStaticField = field;
}
}
if (hasDeclaredVirtualMethod(classType, point))
return false;
for (ICPPBase base : ClassTypeHelper.getBases(classType, point)) {
if (base.isVirtual())
return false;
}
ICPPClassType[] baseClasses = ClassTypeHelper.getAllBases(classType, point);
for (ICPPClassType baseClass : baseClasses) {
if (!isStandardLayout(baseClass, point))
return false;
if (firstNonStaticField != null) {
if (TypeTraits.hasNonStaticFields(baseClass, point))
return false;
if (firstNonStaticField.getType().isSameType(baseClass))
return false;
}
}
return true;
}
/**
* C++11: 9-10
*/
public static boolean isPOD(IType type, IASTNode point) {
if (!isStandardLayout(type, point))
return false;
type = SemanticUtil.getNestedType(type, ARRAY | CVTYPE | TDEF);
if (!(type instanceof ICPPClassType))
return true;
return isTrivial((ICPPClassType) type, point);
}
/**
* Returns true if the given type is a class type, but not a union type, with no non-static
* data members other than bit-fields of length 0, no virtual member functions, no virtual
* base classes, and no base class for which isEmpty is false. [meta.unary.prop]
*/
public static boolean isEmpty(IType type, IASTNode point) {
type = SemanticUtil.getNestedType(type, CVTYPE | TDEF);
if (!(type instanceof ICPPClassType))
return false;
ICPPClassType classType = (ICPPClassType) type;
if (!isItselfEmpty(classType, point))
return false;
ICPPClassType[] baseClasses = ClassTypeHelper.getAllBases(classType, point);
for (ICPPClassType baseClass : baseClasses) {
if (!isItselfEmpty(baseClass, point))
return false;
}
return true;
}
private static boolean isItselfEmpty(ICPPClassType classType, IASTNode point) {
ICPPField[] fields = ClassTypeHelper.getDeclaredFields(classType, point);
for (ICPPField field : fields) {
if (!field.isStatic()) {
// TODO(sprigogin): Check for empty bit fields when bit field size becomes available.
return false;
}
}
ICPPMethod[] methods = ClassTypeHelper.getDeclaredMethods(classType, point);
for (ICPPMethod method : methods) {
if (method.isVirtual())
return false;
}
ICPPBase[] bases = ClassTypeHelper.getBases(classType, point);
for (ICPPBase base : bases) {
if (base.isVirtual())
return false;
}
return true;
}
/**
* 8.5.1 Aggregates [dcl.init.aggr]
* An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1),
* no private or protected non-static data members (Clause 11),
* no base classes (Clause 10), and no virtual functions (10.3).
*/
public static boolean isAggregateClass(ICPPClassType classType, IASTNode point) {
if (ClassTypeHelper.getBases(classType, point).length > 0)
return false;
ICPPMethod[] methods = ClassTypeHelper.getDeclaredMethods(classType, point);
for (ICPPMethod m : methods) {
if (m instanceof ICPPConstructor)
return false;
if (m.isVirtual()) {
return false;
}
}
ICPPField[] fields = ClassTypeHelper.getDeclaredFields(classType, point);
for (ICPPField field : fields) {
if (!(field.getVisibility() == ICPPMember.v_public || field.isStatic())) {
return false;
}
}
return true;
}
/**
* Returns {@code true} if and only if the given class has a trivial copy constructor.
* A copy constructor is trivial if:
* <ul>
* <li>it is implicitly defined by the compiler, and</li>
* <li>{@code isPolymorphic(classType)} is {@code false}, and</li>
* <li>the class has no virtual base classes, and</li>
* <li>every direct base class has trivial copy constructor, and</li>
* <li>for every nonstatic data member that has class type or array of class type, that type
* has trivial copy constructor.</li>
* </ul>
* Similar to {@code std::tr1::has_trivial_copy}.
*
* @param classType the class to check
* @return {@code true} if the class has a trivial copy constructor
*/
public static boolean hasTrivialCopyCtor(ICPPClassType classType, IASTNode point) {
if (getImplicitCopyCtor(classType, point) == null)
return false;
if (isPolymorphic(classType, point))
return false;
for (ICPPBase base : ClassTypeHelper.getBases(classType, point)) {
if (base.isVirtual())
return false;
}
for (ICPPClassType baseClass : ClassTypeHelper.getAllBases(classType, point)) {
if (!classType.isSameType(baseClass) && !hasTrivialCopyCtor(baseClass, point))
return false;
}
for (ICPPField field : ClassTypeHelper.getDeclaredFields(classType, point)) {
if (!field.isStatic()) {
IType type = field.getType();
type = SemanticUtil.getNestedType(type, TDEF | CVTYPE | ARRAY);
if (type instanceof ICPPClassType && !classType.isSameType(type) &&
!hasTrivialCopyCtor((ICPPClassType) type, point)) {
return false;
}
}
}
return true;
}
/**
* Returns {@code true} if and only if the given class has a trivial default constructor.
* A default constructor is trivial if:
* <ul>
* <li>it is implicitly defined by the compiler, and</li>
* <li>every direct base class has trivial default constructor, and</li>
* <li>for every nonstatic data member that has class type or array of class type, that type
* has trivial default constructor.</li>
* </ul>
* Similar to {@code std::tr1::has_trivial_default_constructor}.
*
* @param classType the class to check
* @param point
* @return {@code true} if the class has a trivial default constructor
*/
public static boolean hasTrivialDefaultConstructor(ICPPClassType classType, IASTNode point) {
for (ICPPConstructor ctor : ClassTypeHelper.getConstructors(classType, point)) {
if (!ctor.isImplicit() && ctor.getParameters().length == 0)
return false;
}
for (ICPPClassType baseClass : ClassTypeHelper.getAllBases(classType, null)) {
if (!classType.isSameType(baseClass) && !hasTrivialDefaultConstructor(baseClass, point))
return false;
}
for (ICPPField field : ClassTypeHelper.getDeclaredFields(classType, point)) {
if (!field.isStatic()) {
IType type = field.getType();
type = SemanticUtil.getNestedType(type, TDEF | CVTYPE | ARRAY);
if (type instanceof ICPPClassType && !classType.isSameType(type) &&
!hasTrivialDefaultConstructor((ICPPClassType) type, point)) {
return false;
}
}
}
return true;
}
/**
* Returns {@code true} if and only if the given class has a trivial destructor.
* A destructor is trivial if:
* <ul>
* <li>it is implicitly defined by the compiler or defaulted, and</li>
* <li>every direct base class has trivial destructor, and</li>
* <li>for every nonstatic data member that has class type or array of class type, that type
* has trivial destructor.</li>
* </ul>
* Similar to {@code std::tr1::has_trivial_destructor}.
*
* @param classType the class to check
* @return {@code true} if the class has a trivial destructor
*/
public static boolean hasTrivialDestructor(ICPPClassType classType, IASTNode point) {
return hasTrivialDestructor(classType, point, new HashSet<>());
}
private static boolean hasTrivialDestructor(ICPPClassType classType, IASTNode point,
Set<ICPPClassType> checkedClasses) {
if (!checkedClasses.add(classType))
return true; // Checked already.
for (ICPPMethod method : ClassTypeHelper.getDeclaredMethods(classType, point)) {
if (method.isDestructor() && !isDefaultedMethod(method))
return false;
}
for (ICPPClassType baseClass : ClassTypeHelper.getAllBases(classType, point)) {
if (!hasTrivialDestructor(baseClass, point, checkedClasses))
return false;
}
for (ICPPField field : ClassTypeHelper.getDeclaredFields(classType, point)) {
if (!field.isStatic()) {
IType type = field.getType();
type = SemanticUtil.getNestedType(type, TDEF | CVTYPE | ARRAY);
if (type instanceof ICPPClassType &&
!hasTrivialDestructor((ICPPClassType) type, point, checkedClasses)) {
return false;
}
}
}
return true;
}
/**
* Returns {@code true} if and only if the given class declares or inherits a virtual
* function. Similar to {@code std::tr1::is_polymorphic}.
*
* @param classType the class to check
* @return {@code true} if the class declares or inherits a virtual function.
*/
public static boolean isPolymorphic(ICPPClassType classType, IASTNode point) {
if (hasDeclaredVirtualMethod(classType, point))
return true;
for (ICPPClassType baseClass : ClassTypeHelper.getAllBases(classType, point)) {
if (hasDeclaredVirtualMethod(baseClass, point))
return true;
}
return false;
}
private static boolean hasNonStaticFields(ICPPClassType classType, IASTNode point) {
ICPPField[] fields = ClassTypeHelper.getDeclaredFields(classType, point);
for (ICPPField field : fields) {
if (!field.isStatic())
return true;
}
return false;
}
public static boolean isAbstract(ICPPClassType classType, IASTNode point) {
return SemanticQueries.getPureVirtualMethods(classType, point).length != 0;
}
/**
* Returns the compiler-generated copy constructor for the given class, or {@code null}
* if the class doesn't have a compiler-generated copy constructor.
*
* @param classType the class to get the copy ctor for.
* @return the compiler-generated copy constructor, or {@code null} if the class doesn't
* have a compiler-generated copy constructor.
*/
private static ICPPConstructor getImplicitCopyCtor(ICPPClassType classType, IASTNode point) {
for (ICPPConstructor ctor : ClassTypeHelper.getConstructors(classType, point)) {
if (ctor.isImplicit() && ClassTypeHelper.getMethodKind(classType, ctor) == MethodKind.COPY_CTOR)
return ctor;
}
return null;
}
private static boolean hasDeclaredVirtualMethod(ICPPClassType classType, IASTNode point) {
for (ICPPMethod method : ClassTypeHelper.getDeclaredMethods(classType, point)) {
if (method.isVirtual()) {
return true;
}
}
return false;
}
public static IType underlyingType(IType type) {
if (CPPTemplates.isDependentType(type)) {
return new CPPUnaryTypeTransformation(Operator.underlying_type, type);
} else if (!(type instanceof ICPPEnumeration)) {
return ProblemType.ENUMERATION_EXPECTED;
} else {
ICPPEnumeration enumeration = (ICPPEnumeration) type;
IType fixedType = enumeration.getFixedType();
if (fixedType != null)
return fixedType;
// [dcl.enum] 7.2-6:
// "For an enumeration whose underlying type is not fixed, the
// underlying type is an integral type that can represent all
// the numerator values defined in the enumeration. ... It is
// implementation-defined which integral type is used as the
// underlying type except that the underlying type shall not be
// larger than int unless the value of an enumerator cannot fit
// in an int or unsigned int. If the enumerator-list is empty,
// the underlying type is as if the enumeration had a single
// enumerator with value 0."
if (enumeration.getEnumerators().length == 0)
return CPPBasicType.INT;
long minValue = enumeration.getMinValue();
long maxValue = enumeration.getMaxValue();
if (minValue < 0 || maxValue < 0) {
return smallestFittingType(minValue, maxValue, SIGNED_UNDERLYING_ENUM_TYPES);
} else {
return smallestFittingType(minValue, maxValue, UNSIGNED_UNDERLYING_ENUM_TYPES);
}
}
}
private static IBasicType smallestFittingType(long minValue, long maxValue, ICPPBasicType[] types) {
for (ICPPBasicType type : types) {
if (ArithmeticConversion.fitsIntoType(type, minValue)
&& ArithmeticConversion.fitsIntoType(type, maxValue)) {
return type;
}
}
return types[types.length - 1]; // Assume it fits into the largest type provided.
}
/**
* Returns true if 'type' is scalar, as defined in [basic.types] p9:
*
* "Arithmetic types, enumeration types, pointer types, pointer to member
* types, std::nullptr_t, and cv-qualified versions of these types are
* collectively called scalar types."
*/
private static boolean isScalar(IType type) {
type = SemanticUtil.getNestedType(type, SemanticUtil.ALLCVQ);
return type instanceof IBasicType || type instanceof IEnumeration || type instanceof IPointerType;
}
/**
* Returns true if 'type' is a trivially copyable class, as defined in [class] p6:
*
* "A trivially copyable class is a class that:
* - has no non-trivial copy constructors,
* - has no non-trivial move constructors,
* - has no non-trivial copy assignment operators,
* - has no non-trivial move assignment operators, and
* - has a trivial destructor."
*/
private static boolean isTriviallyCopyableClass(ICPPClassType type, IASTNode point) {
return isTrivialImpl(type, point, false);
}
/**
* Returns true if 'type' is trivially copyable, as defined in [basic.types] p9:
*
* "Cv-unqualified scalar types, trivially copyable class types, arrays
* of such types, and non-volatile const-qualified versions of these
* types are collectively called trivially copyable types."
*/
public static boolean isTriviallyCopyable(IType type, IASTNode point) {
type = SemanticUtil.getSimplifiedType(type);
CVQualifier qualifier = SemanticUtil.getCVQualifier(type);
if (qualifier.isVolatile()) {
return false;
} else if (qualifier.isConst()) {
return isTriviallyCopyable(SemanticUtil.getNestedType(type, SemanticUtil.ALLCVQ), point);
} else if (type instanceof IArrayType) {
return isTriviallyCopyable(((IArrayType) type).getType(), point);
} else if (type instanceof ICPPClassType) {
return isTriviallyCopyableClass((ICPPClassType) type, point);
} else {
return isScalar(type);
}
}
}