Rewrite type compatibility testing to do type merging rather than just

testing compatibility.  This is necessary for some constructs, like merging
redeclarations.

Also, there are some ObjC changes to make sure that 
typesAreCompatible(a,b) == typesAreCompatible(b,a).  I don't have any 
ObjC code beyond the testsuite, so please tell me if there are any cases 
where this doesn't behave as expected.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@55158 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Eli Friedman 2008-08-22 00:56:42 +00:00
Родитель d8bfe7f25a
Коммит 3d815e7eb5
4 изменённых файлов: 216 добавлений и 158 удалений

Просмотреть файл

@ -397,9 +397,6 @@ public:
/// Compatibility predicates used to check assignment expressions.
bool typesAreCompatible(QualType, QualType); // C99 6.2.7p1
bool pointerTypesAreCompatible(QualType, QualType); // C99 6.7.5.1p2
bool referenceTypesAreCompatible(QualType, QualType); // C++ 5.17p6
bool functionTypesAreCompatible(QualType, QualType); // C99 6.7.5.3p15
bool isObjCIdType(QualType T) const {
if (!IdStructType) // ObjC isn't enabled
@ -416,6 +413,14 @@ public:
return T->getAsStructureType() == SelStructType;
}
// Check the safety of assignment from LHS to RHS
bool canAssignObjCInterfaces(const ObjCInterfaceType *LHS,
const ObjCInterfaceType *RHS);
// Functions for calculating composite types
QualType mergeTypes(QualType, QualType);
QualType mergeFunctionTypes(QualType, QualType);
//===--------------------------------------------------------------------===//
// Integer Predicates
//===--------------------------------------------------------------------===//

Просмотреть файл

@ -1623,92 +1623,6 @@ bool ASTContext::isObjCObjectPointerType(QualType Ty) const {
// Type Compatibility Testing
//===----------------------------------------------------------------------===//
/// C99 6.2.7p1: If both are complete types, then the following additional
/// requirements apply.
/// FIXME (handle compatibility across source files).
static bool areCompatTagTypes(TagType *LHS, TagType *RHS,
const ASTContext &C) {
// "Class" and "id" are compatible built-in structure types.
if (C.isObjCIdType(QualType(LHS, 0)) && C.isObjCClassType(QualType(RHS, 0)) ||
C.isObjCClassType(QualType(LHS, 0)) && C.isObjCIdType(QualType(RHS, 0)))
return true;
// Within a translation unit a tag type is only compatible with itself. Self
// equality is already handled by the time we get here.
assert(LHS != RHS && "Self equality not handled!");
return false;
}
bool ASTContext::pointerTypesAreCompatible(QualType lhs, QualType rhs) {
// C99 6.7.5.1p2: For two pointer types to be compatible, both shall be
// identically qualified and both shall be pointers to compatible types.
if (lhs.getCVRQualifiers() != rhs.getCVRQualifiers() ||
lhs.getAddressSpace() != rhs.getAddressSpace())
return false;
QualType ltype = lhs->getAsPointerType()->getPointeeType();
QualType rtype = rhs->getAsPointerType()->getPointeeType();
return typesAreCompatible(ltype, rtype);
}
bool ASTContext::functionTypesAreCompatible(QualType lhs, QualType rhs) {
const FunctionType *lbase = lhs->getAsFunctionType();
const FunctionType *rbase = rhs->getAsFunctionType();
const FunctionTypeProto *lproto = dyn_cast<FunctionTypeProto>(lbase);
const FunctionTypeProto *rproto = dyn_cast<FunctionTypeProto>(rbase);
// first check the return types (common between C99 and K&R).
if (!typesAreCompatible(lbase->getResultType(), rbase->getResultType()))
return false;
if (lproto && rproto) { // two C99 style function prototypes
unsigned lproto_nargs = lproto->getNumArgs();
unsigned rproto_nargs = rproto->getNumArgs();
if (lproto_nargs != rproto_nargs)
return false;
// both prototypes have the same number of arguments.
if ((lproto->isVariadic() && !rproto->isVariadic()) ||
(rproto->isVariadic() && !lproto->isVariadic()))
return false;
// The use of ellipsis agree...now check the argument types.
for (unsigned i = 0; i < lproto_nargs; i++)
// C99 6.7.5.3p15: ...and each parameter declared with qualified type
// is taken as having the unqualified version of it's declared type.
if (!typesAreCompatible(lproto->getArgType(i).getUnqualifiedType(),
rproto->getArgType(i).getUnqualifiedType()))
return false;
return true;
}
if (!lproto && !rproto) // two K&R style function decls, nothing to do.
return true;
// we have a mixture of K&R style with C99 prototypes
const FunctionTypeProto *proto = lproto ? lproto : rproto;
if (proto->isVariadic())
return false;
// FIXME: Each parameter type T in the prototype must be compatible with the
// type resulting from applying the usual argument conversions to T.
return true;
}
// C99 6.7.5.2p6
static bool areCompatArrayTypes(ArrayType *LHS, ArrayType *RHS, ASTContext &C) {
// Constant arrays must be the same size to be compatible.
if (const ConstantArrayType* LCAT = dyn_cast<ConstantArrayType>(LHS))
if (const ConstantArrayType* RCAT = dyn_cast<ConstantArrayType>(RHS))
if (RCAT->getSize() != LCAT->getSize())
return false;
// Compatible arrays must have compatible element types
return C.typesAreCompatible(LHS->getElementType(), RHS->getElementType());
}
/// areCompatVectorTypes - Return true if the two specified vector types are
/// compatible.
static bool areCompatVectorTypes(const VectorType *LHS,
@ -1718,12 +1632,12 @@ static bool areCompatVectorTypes(const VectorType *LHS,
LHS->getNumElements() == RHS->getNumElements();
}
/// areCompatObjCInterfaces - Return true if the two interface types are
/// canAssignObjCInterfaces - Return true if the two interface types are
/// compatible for assignment from RHS to LHS. This handles validation of any
/// protocol qualifiers on the LHS or RHS.
///
static bool areCompatObjCInterfaces(const ObjCInterfaceType *LHS,
const ObjCInterfaceType *RHS) {
bool ASTContext::canAssignObjCInterfaces(const ObjCInterfaceType *LHS,
const ObjCInterfaceType *RHS) {
// Verify that the base decls are compatible: the RHS must be a subclass of
// the LHS.
if (!LHS->getDecl()->isSuperClassOf(RHS->getDecl()))
@ -1771,41 +1685,113 @@ static bool areCompatObjCInterfaces(const ObjCInterfaceType *LHS,
return false;
}
/// typesAreCompatible - C99 6.7.3p9: For two qualified types to be compatible,
/// both shall have the identically qualified version of a compatible type.
/// C99 6.2.7p1: Two types have compatible types if their types are the
/// same. See 6.7.[2,3,5] for additional rules.
bool ASTContext::typesAreCompatible(QualType LHS_NC, QualType RHS_NC) {
QualType LHS = getCanonicalType(LHS_NC);
QualType RHS = getCanonicalType(RHS_NC);
bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS) {
return !mergeTypes(LHS, RHS).isNull();
}
QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs) {
const FunctionType *lbase = lhs->getAsFunctionType();
const FunctionType *rbase = rhs->getAsFunctionType();
const FunctionTypeProto *lproto = dyn_cast<FunctionTypeProto>(lbase);
const FunctionTypeProto *rproto = dyn_cast<FunctionTypeProto>(rbase);
bool allLTypes = true;
bool allRTypes = true;
// Check return type
QualType retType = mergeTypes(lbase->getResultType(), rbase->getResultType());
if (retType.isNull()) return QualType();
if (getCanonicalType(retType) != getCanonicalType(lbase->getResultType())) allLTypes = false;
if (getCanonicalType(retType) != getCanonicalType(rbase->getResultType())) allRTypes = false;
if (lproto && rproto) { // two C99 style function prototypes
unsigned lproto_nargs = lproto->getNumArgs();
unsigned rproto_nargs = rproto->getNumArgs();
// Compatible functions must have the same number of arguments
if (lproto_nargs != rproto_nargs)
return QualType();
// Variadic and non-variadic functions aren't compatible
if (lproto->isVariadic() != rproto->isVariadic())
return QualType();
// Check argument compatibility
llvm::SmallVector<QualType, 10> types;
for (unsigned i = 0; i < lproto_nargs; i++) {
QualType largtype = lproto->getArgType(i).getUnqualifiedType();
QualType rargtype = rproto->getArgType(i).getUnqualifiedType();
QualType argtype = mergeTypes(largtype, rargtype);
if (argtype.isNull()) return QualType();
types.push_back(argtype);
if (getCanonicalType(argtype) != getCanonicalType(largtype)) allLTypes = false;
if (getCanonicalType(argtype) != getCanonicalType(rargtype)) allRTypes = false;
}
if (allLTypes) return lhs;
if (allRTypes) return rhs;
return getFunctionType(retType, types.begin(), types.size(),
lproto->isVariadic());
}
if (lproto) allRTypes = false;
if (rproto) allLTypes = false;
const FunctionTypeProto *proto = lproto ? lproto : rproto;
if (proto) {
if (proto->isVariadic()) return QualType();
// Check that the types are compatible with the types that
// would result from default argument promotions (C99 6.7.5.3p15).
// The only types actually affected are promotable integer
// types and floats, which would be passed as a different
// type depending on whether the prototype is visible.
unsigned proto_nargs = proto->getNumArgs();
for (unsigned i = 0; i < proto_nargs; ++i) {
QualType argTy = proto->getArgType(i);
if (argTy->isPromotableIntegerType() ||
getCanonicalType(argTy).getUnqualifiedType() == FloatTy)
return QualType();
}
if (allLTypes) return lhs;
if (allRTypes) return rhs;
return getFunctionType(retType, proto->arg_type_begin(),
proto->getNumArgs(), lproto->isVariadic());
}
if (allLTypes) return lhs;
if (allRTypes) return rhs;
return getFunctionTypeNoProto(retType);
}
QualType ASTContext::mergeTypes(QualType LHS, QualType RHS) {
// C++ [expr]: If an expression initially has the type "reference to T", the
// type is adjusted to "T" prior to any further analysis, the expression
// designates the object or function denoted by the reference, and the
// expression is an lvalue.
if (ReferenceType *RT = dyn_cast<ReferenceType>(LHS))
// FIXME: C++ shouldn't be going through here! The rules are different
// enough that they should be handled separately.
if (const ReferenceType *RT = LHS->getAsReferenceType())
LHS = RT->getPointeeType();
if (ReferenceType *RT = dyn_cast<ReferenceType>(RHS))
if (const ReferenceType *RT = RHS->getAsReferenceType())
RHS = RT->getPointeeType();
QualType LHSCan = getCanonicalType(LHS),
RHSCan = getCanonicalType(RHS);
// If two types are identical, they are compatible.
if (LHS == RHS)
return true;
if (LHSCan == RHSCan)
return LHS;
// If qualifiers differ, the types are different.
unsigned LHSAS = LHS.getAddressSpace(), RHSAS = RHS.getAddressSpace();
if (LHS.getCVRQualifiers() != RHS.getCVRQualifiers() || LHSAS != RHSAS)
return false;
// If the qualifiers are different, the types aren't compatible
if (LHSCan.getCVRQualifiers() != RHSCan.getCVRQualifiers() ||
LHSCan.getAddressSpace() != RHSCan.getAddressSpace())
return QualType();
// Strip off ASQual's if present.
if (LHSAS) {
LHS = LHS.getUnqualifiedType();
RHS = RHS.getUnqualifiedType();
}
Type::TypeClass LHSClass = LHS->getTypeClass();
Type::TypeClass RHSClass = RHS->getTypeClass();
Type::TypeClass LHSClass = LHSCan->getTypeClass();
Type::TypeClass RHSClass = RHSCan->getTypeClass();
// We want to consider the two function types to be the same for these
// comparisons, just force one to the other.
@ -1828,65 +1814,103 @@ bool ASTContext::typesAreCompatible(QualType LHS_NC, QualType RHS_NC) {
// If the canonical type classes don't match.
if (LHSClass != RHSClass) {
// ID is compatible with all interface types.
if (isa<ObjCInterfaceType>(LHS))
return isObjCIdType(RHS);
if (isa<ObjCInterfaceType>(RHS))
return isObjCIdType(LHS);
// ID is compatible with all qualified id types.
if (isa<ObjCQualifiedIdType>(LHS)) {
if (LHS->isObjCQualifiedIdType()) {
if (const PointerType *PT = RHS->getAsPointerType())
return isObjCIdType(PT->getPointeeType());
if (isObjCIdType(PT->getPointeeType()))
return LHS;
}
if (isa<ObjCQualifiedIdType>(RHS)) {
if (RHS->isObjCQualifiedIdType()) {
if (const PointerType *PT = LHS->getAsPointerType())
return isObjCIdType(PT->getPointeeType());
if (isObjCIdType(PT->getPointeeType()))
return RHS;
}
// C99 6.7.2.2p4: Each enumerated type shall be compatible with char,
// a signed integer type, or an unsigned integer type.
if (LHS->isEnumeralType() && RHS->isIntegralType()) {
EnumDecl* EDecl = cast<EnumType>(LHS)->getDecl();
return EDecl->getIntegerType() == RHS;
if (const EnumType* ETy = LHS->getAsEnumType()) {
if (ETy->getDecl()->getIntegerType() == RHSCan.getUnqualifiedType())
return RHS;
}
if (RHS->isEnumeralType() && LHS->isIntegralType()) {
EnumDecl* EDecl = cast<EnumType>(RHS)->getDecl();
return EDecl->getIntegerType() == LHS;
if (const EnumType* ETy = RHS->getAsEnumType()) {
if (ETy->getDecl()->getIntegerType() == LHSCan.getUnqualifiedType())
return LHS;
}
return false;
return QualType();
}
// The canonical type classes match.
switch (LHSClass) {
case Type::ASQual:
case Type::FunctionProto:
case Type::VariableArray:
case Type::IncompleteArray:
case Type::Reference:
case Type::ObjCQualifiedInterface:
assert(0 && "Canonicalized away above");
case Type::Pointer:
return pointerTypesAreCompatible(LHS, RHS);
{
// Merge two pointer types, while trying to preserve typedef info
QualType LHSPointee = LHS->getAsPointerType()->getPointeeType();
QualType RHSPointee = RHS->getAsPointerType()->getPointeeType();
QualType ResultType = mergeTypes(LHSPointee, RHSPointee);
if (ResultType.isNull()) return QualType();
if (getCanonicalType(LHSPointee) != getCanonicalType(ResultType)) return LHS;
if (getCanonicalType(RHSPointee) != getCanonicalType(ResultType)) return RHS;
return getPointerType(ResultType);
}
case Type::ConstantArray:
return areCompatArrayTypes(cast<ArrayType>(LHS), cast<ArrayType>(RHS),
*this);
{
const ConstantArrayType* LCAT = getAsConstantArrayType(LHS);
const ConstantArrayType* RCAT = getAsConstantArrayType(RHS);
if (LCAT && RCAT && RCAT->getSize() != LCAT->getSize())
return QualType();
QualType LHSElem = getAsArrayType(LHS)->getElementType();
QualType RHSElem = getAsArrayType(RHS)->getElementType();
QualType ResultType = mergeTypes(LHSElem, RHSElem);
if (ResultType.isNull()) return QualType();
if (LCAT && getCanonicalType(LHSElem) != getCanonicalType(ResultType)) return LHS;
if (RCAT && getCanonicalType(RHSElem) != getCanonicalType(ResultType)) return RHS;
const VariableArrayType* LVAT = getAsVariableArrayType(LHS);
const VariableArrayType* RVAT = getAsVariableArrayType(RHS);
if (LVAT && getCanonicalType(LHSElem) != getCanonicalType(ResultType)) return LHS;
if (RVAT && getCanonicalType(RHSElem) != getCanonicalType(ResultType)) return RHS;
if (LVAT) {
// FIXME: This isn't correct! But tricky to implement because
// the array's size has to be the size of LHS, but the type
// has to be different.
return LHS;
}
if (RVAT) {
// FIXME: This isn't correct! But tricky to implement because
// the array's size has to be the size of RHS, but the type
// has to be different.
return RHS;
}
if (getCanonicalType(LHSElem) != getCanonicalType(ResultType)) return LHS;
if (getCanonicalType(RHSElem) != getCanonicalType(ResultType)) return RHS;
return getIncompleteArrayType(ResultType, ArrayType::ArraySizeModifier(), 0);
}
case Type::FunctionNoProto:
return functionTypesAreCompatible(LHS, RHS);
case Type::Tagged: // handle structures, unions
return areCompatTagTypes(cast<TagType>(LHS), cast<TagType>(RHS), *this);
return mergeFunctionTypes(LHS, RHS);
case Type::Tagged:
{
// FIXME: Why are these compatible?
if (isObjCIdType(LHS) && isObjCClassType(RHS)) return LHS;
if (isObjCClassType(LHS) && isObjCIdType(RHS)) return LHS;
return QualType();
}
case Type::Builtin:
// Only exactly equal builtin types are compatible, which is tested above.
return false;
return QualType();
case Type::Vector:
return areCompatVectorTypes(cast<VectorType>(LHS), cast<VectorType>(RHS));
if (areCompatVectorTypes(LHS->getAsVectorType(), RHS->getAsVectorType()))
return LHS;
case Type::ObjCInterface:
return areCompatObjCInterfaces(cast<ObjCInterfaceType>(LHS),
cast<ObjCInterfaceType>(RHS));
{
// Distinct ObjC interfaces are not compatible; see canAssignObjCInterfaces
// for checking assignment/comparison safety
return QualType();
}
default:
assert(0 && "unexpected type");
return QualType();
}
return true; // should never get here...
}
//===----------------------------------------------------------------------===//

Просмотреть файл

@ -346,7 +346,7 @@ Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, bool &Redeclaration) {
// C: Function types need to be compatible, not identical. This handles
// duplicate function decls like "void f(int); void f(enum X);" properly.
if (!getLangOptions().CPlusPlus &&
Context.functionTypesAreCompatible(OldQType, NewQType)) {
Context.typesAreCompatible(OldQType, NewQType)) {
MergeAttributes(New, Old);
Redeclaration = true;
return New;

Просмотреть файл

@ -1355,6 +1355,19 @@ Sema::CheckPointerTypesForAssignment(QualType lhsType, QualType rhsType) {
return FunctionVoidPointer;
}
// Check for ObjC interfaces
const ObjCInterfaceType* LHSIface = lhptee->getAsObjCInterfaceType();
const ObjCInterfaceType* RHSIface = rhptee->getAsObjCInterfaceType();
if (LHSIface && RHSIface &&
Context.canAssignObjCInterfaces(LHSIface, RHSIface))
return ConvTy;
// ID acts sort of like void* for ObjC interfaces
if (LHSIface && Context.isObjCIdType(rhptee))
return ConvTy;
if (RHSIface && Context.isObjCIdType(lhptee))
return ConvTy;
// C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or
// unqualified versions of compatible types, ...
if (!Context.typesAreCompatible(lhptee.getUnqualifiedType(),
@ -1701,6 +1714,21 @@ QualType Sema::CheckShiftOperands(Expr *&lex, Expr *&rex, SourceLocation loc,
return lex->getType();
}
static bool areComparableObjCInterfaces(QualType LHS, QualType RHS,
ASTContext& Context) {
const ObjCInterfaceType* LHSIface = LHS->getAsObjCInterfaceType();
const ObjCInterfaceType* RHSIface = RHS->getAsObjCInterfaceType();
// ID acts sort of like void* for ObjC interfaces
if (LHSIface && Context.isObjCIdType(RHS))
return true;
if (RHSIface && Context.isObjCIdType(LHS))
return true;
if (!LHSIface || !RHSIface)
return false;
return Context.canAssignObjCInterfaces(LHSIface, RHSIface) ||
Context.canAssignObjCInterfaces(RHSIface, LHSIface);
}
// C99 6.5.8
QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation loc,
bool isRelational) {
@ -1756,7 +1784,8 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation loc,
if (!LHSIsNull && !RHSIsNull && // C99 6.5.9p2
!LCanPointeeTy->isVoidType() && !RCanPointeeTy->isVoidType() &&
!Context.typesAreCompatible(LCanPointeeTy.getUnqualifiedType(),
RCanPointeeTy.getUnqualifiedType())) {
RCanPointeeTy.getUnqualifiedType()) &&
!areComparableObjCInterfaces(LCanPointeeTy, RCanPointeeTy, Context)) {
Diag(loc, diag::ext_typecheck_comparison_of_distinct_pointers,
lType.getAsString(), rType.getAsString(),
lex->getSourceRange(), rex->getSourceRange());