[Clang] [C23] Fix typeof_unqual for qualified array types (#92767)
Properly remove qualifiers for both the element type and the array type Fixes #92667 --------- Co-authored-by: cor3ntin <corentinjabot@gmail.com>
This commit is contained in:
parent
c034c44362
commit
a56e009ef8
@ -838,6 +838,8 @@ Bug Fixes in This Version
|
|||||||
- ``__has_unique_object_representations`` correctly handles arrays of unknown bounds of
|
- ``__has_unique_object_representations`` correctly handles arrays of unknown bounds of
|
||||||
types by ensuring they are complete and instantiating them if needed. Fixes (#GH95311).
|
types by ensuring they are complete and instantiating them if needed. Fixes (#GH95311).
|
||||||
|
|
||||||
|
- ``typeof_unqual`` now properly removes type qualifiers from arrays and their element types. (#GH92667)
|
||||||
|
|
||||||
Bug Fixes to Compiler Builtins
|
Bug Fixes to Compiler Builtins
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -2653,6 +2653,10 @@ public:
|
|||||||
/// \returns if this is an array type, the completely unqualified array type
|
/// \returns if this is an array type, the completely unqualified array type
|
||||||
/// that corresponds to it. Otherwise, returns T.getUnqualifiedType().
|
/// that corresponds to it. Otherwise, returns T.getUnqualifiedType().
|
||||||
QualType getUnqualifiedArrayType(QualType T, Qualifiers &Quals) const;
|
QualType getUnqualifiedArrayType(QualType T, Qualifiers &Quals) const;
|
||||||
|
QualType getUnqualifiedArrayType(QualType T) const {
|
||||||
|
Qualifiers Quals;
|
||||||
|
return getUnqualifiedArrayType(T, Quals);
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine whether the given types are equivalent after
|
/// Determine whether the given types are equivalent after
|
||||||
/// cvr-qualifiers have been removed.
|
/// cvr-qualifiers have been removed.
|
||||||
|
@ -1618,6 +1618,10 @@ public:
|
|||||||
QualType stripObjCKindOfType(const ASTContext &ctx) const;
|
QualType stripObjCKindOfType(const ASTContext &ctx) const;
|
||||||
|
|
||||||
/// Remove all qualifiers including _Atomic.
|
/// Remove all qualifiers including _Atomic.
|
||||||
|
///
|
||||||
|
/// Like getUnqualifiedType(), the type may still be qualified if it is a
|
||||||
|
/// sugared array type. To strip qualifiers even from within a sugared array
|
||||||
|
/// type, use in conjunction with ASTContext::getUnqualifiedArrayType.
|
||||||
QualType getAtomicUnqualifiedType() const;
|
QualType getAtomicUnqualifiedType() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -2105,8 +2109,8 @@ protected:
|
|||||||
|
|
||||||
LLVM_PREFERRED_TYPE(TypeBitfields)
|
LLVM_PREFERRED_TYPE(TypeBitfields)
|
||||||
unsigned : NumTypeBits;
|
unsigned : NumTypeBits;
|
||||||
LLVM_PREFERRED_TYPE(bool)
|
LLVM_PREFERRED_TYPE(TypeOfKind)
|
||||||
unsigned IsUnqual : 1; // If true: typeof_unqual, else: typeof
|
unsigned Kind : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UsingBitfields {
|
class UsingBitfields {
|
||||||
@ -5661,19 +5665,20 @@ public:
|
|||||||
/// extension) or a `typeof_unqual` expression (a C23 feature).
|
/// extension) or a `typeof_unqual` expression (a C23 feature).
|
||||||
class TypeOfExprType : public Type {
|
class TypeOfExprType : public Type {
|
||||||
Expr *TOExpr;
|
Expr *TOExpr;
|
||||||
|
const ASTContext &Context;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class ASTContext; // ASTContext creates these.
|
friend class ASTContext; // ASTContext creates these.
|
||||||
|
|
||||||
TypeOfExprType(Expr *E, TypeOfKind Kind, QualType Can = QualType());
|
TypeOfExprType(const ASTContext &Context, Expr *E, TypeOfKind Kind,
|
||||||
|
QualType Can = QualType());
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Expr *getUnderlyingExpr() const { return TOExpr; }
|
Expr *getUnderlyingExpr() const { return TOExpr; }
|
||||||
|
|
||||||
/// Returns the kind of 'typeof' type this is.
|
/// Returns the kind of 'typeof' type this is.
|
||||||
TypeOfKind getKind() const {
|
TypeOfKind getKind() const {
|
||||||
return TypeOfBits.IsUnqual ? TypeOfKind::Unqualified
|
return static_cast<TypeOfKind>(TypeOfBits.Kind);
|
||||||
: TypeOfKind::Qualified;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a single level of sugar.
|
/// Remove a single level of sugar.
|
||||||
@ -5694,7 +5699,8 @@ public:
|
|||||||
class DependentTypeOfExprType : public TypeOfExprType,
|
class DependentTypeOfExprType : public TypeOfExprType,
|
||||||
public llvm::FoldingSetNode {
|
public llvm::FoldingSetNode {
|
||||||
public:
|
public:
|
||||||
DependentTypeOfExprType(Expr *E, TypeOfKind Kind) : TypeOfExprType(E, Kind) {}
|
DependentTypeOfExprType(const ASTContext &Context, Expr *E, TypeOfKind Kind)
|
||||||
|
: TypeOfExprType(Context, E, Kind) {}
|
||||||
|
|
||||||
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
|
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
|
||||||
Profile(ID, Context, getUnderlyingExpr(),
|
Profile(ID, Context, getUnderlyingExpr(),
|
||||||
@ -5711,32 +5717,23 @@ class TypeOfType : public Type {
|
|||||||
friend class ASTContext; // ASTContext creates these.
|
friend class ASTContext; // ASTContext creates these.
|
||||||
|
|
||||||
QualType TOType;
|
QualType TOType;
|
||||||
|
const ASTContext &Context;
|
||||||
|
|
||||||
TypeOfType(QualType T, QualType Can, TypeOfKind Kind)
|
TypeOfType(const ASTContext &Context, QualType T, QualType Can,
|
||||||
: Type(TypeOf,
|
TypeOfKind Kind);
|
||||||
Kind == TypeOfKind::Unqualified ? Can.getAtomicUnqualifiedType()
|
|
||||||
: Can,
|
|
||||||
T->getDependence()),
|
|
||||||
TOType(T) {
|
|
||||||
TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QualType getUnmodifiedType() const { return TOType; }
|
QualType getUnmodifiedType() const { return TOType; }
|
||||||
|
|
||||||
/// Remove a single level of sugar.
|
/// Remove a single level of sugar.
|
||||||
QualType desugar() const {
|
QualType desugar() const;
|
||||||
QualType QT = getUnmodifiedType();
|
|
||||||
return TypeOfBits.IsUnqual ? QT.getAtomicUnqualifiedType() : QT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether this type directly provides sugar.
|
/// Returns whether this type directly provides sugar.
|
||||||
bool isSugared() const { return true; }
|
bool isSugared() const { return true; }
|
||||||
|
|
||||||
/// Returns the kind of 'typeof' type this is.
|
/// Returns the kind of 'typeof' type this is.
|
||||||
TypeOfKind getKind() const {
|
TypeOfKind getKind() const {
|
||||||
return TypeOfBits.IsUnqual ? TypeOfKind::Unqualified
|
return static_cast<TypeOfKind>(TypeOfBits.Kind);
|
||||||
: TypeOfKind::Qualified;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool classof(const Type *T) { return T->getTypeClass() == TypeOf; }
|
static bool classof(const Type *T) { return T->getTypeClass() == TypeOf; }
|
||||||
|
@ -6020,19 +6020,19 @@ QualType ASTContext::getTypeOfExprType(Expr *tofExpr, TypeOfKind Kind) const {
|
|||||||
if (Canon) {
|
if (Canon) {
|
||||||
// We already have a "canonical" version of an identical, dependent
|
// We already have a "canonical" version of an identical, dependent
|
||||||
// typeof(expr) type. Use that as our canonical type.
|
// typeof(expr) type. Use that as our canonical type.
|
||||||
toe = new (*this, alignof(TypeOfExprType))
|
toe = new (*this, alignof(TypeOfExprType)) TypeOfExprType(
|
||||||
TypeOfExprType(tofExpr, Kind, QualType((TypeOfExprType *)Canon, 0));
|
*this, tofExpr, Kind, QualType((TypeOfExprType *)Canon, 0));
|
||||||
} else {
|
} else {
|
||||||
// Build a new, canonical typeof(expr) type.
|
// Build a new, canonical typeof(expr) type.
|
||||||
Canon = new (*this, alignof(DependentTypeOfExprType))
|
Canon = new (*this, alignof(DependentTypeOfExprType))
|
||||||
DependentTypeOfExprType(tofExpr, Kind);
|
DependentTypeOfExprType(*this, tofExpr, Kind);
|
||||||
DependentTypeOfExprTypes.InsertNode(Canon, InsertPos);
|
DependentTypeOfExprTypes.InsertNode(Canon, InsertPos);
|
||||||
toe = Canon;
|
toe = Canon;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QualType Canonical = getCanonicalType(tofExpr->getType());
|
QualType Canonical = getCanonicalType(tofExpr->getType());
|
||||||
toe = new (*this, alignof(TypeOfExprType))
|
toe = new (*this, alignof(TypeOfExprType))
|
||||||
TypeOfExprType(tofExpr, Kind, Canonical);
|
TypeOfExprType(*this, tofExpr, Kind, Canonical);
|
||||||
}
|
}
|
||||||
Types.push_back(toe);
|
Types.push_back(toe);
|
||||||
return QualType(toe, 0);
|
return QualType(toe, 0);
|
||||||
@ -6045,8 +6045,8 @@ QualType ASTContext::getTypeOfExprType(Expr *tofExpr, TypeOfKind Kind) const {
|
|||||||
/// on canonical types (which are always unique).
|
/// on canonical types (which are always unique).
|
||||||
QualType ASTContext::getTypeOfType(QualType tofType, TypeOfKind Kind) const {
|
QualType ASTContext::getTypeOfType(QualType tofType, TypeOfKind Kind) const {
|
||||||
QualType Canonical = getCanonicalType(tofType);
|
QualType Canonical = getCanonicalType(tofType);
|
||||||
auto *tot =
|
auto *tot = new (*this, alignof(TypeOfType))
|
||||||
new (*this, alignof(TypeOfType)) TypeOfType(tofType, Canonical, Kind);
|
TypeOfType(*this, tofType, Canonical, Kind);
|
||||||
Types.push_back(tot);
|
Types.push_back(tot);
|
||||||
return QualType(tot, 0);
|
return QualType(tot, 0);
|
||||||
}
|
}
|
||||||
|
@ -1627,9 +1627,10 @@ QualType QualType::stripObjCKindOfType(const ASTContext &constCtx) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QualType QualType::getAtomicUnqualifiedType() const {
|
QualType QualType::getAtomicUnqualifiedType() const {
|
||||||
if (const auto AT = getTypePtr()->getAs<AtomicType>())
|
QualType T = *this;
|
||||||
return AT->getValueType().getUnqualifiedType();
|
if (const auto AT = T.getTypePtr()->getAs<AtomicType>())
|
||||||
return getUnqualifiedType();
|
T = AT->getValueType();
|
||||||
|
return T.getUnqualifiedType();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ArrayRef<QualType>>
|
std::optional<ArrayRef<QualType>>
|
||||||
@ -3890,18 +3891,19 @@ QualType MacroQualifiedType::getModifiedType() const {
|
|||||||
return Inner;
|
return Inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeOfExprType::TypeOfExprType(Expr *E, TypeOfKind Kind, QualType Can)
|
TypeOfExprType::TypeOfExprType(const ASTContext &Context, Expr *E,
|
||||||
|
TypeOfKind Kind, QualType Can)
|
||||||
: Type(TypeOfExpr,
|
: Type(TypeOfExpr,
|
||||||
// We have to protect against 'Can' being invalid through its
|
// We have to protect against 'Can' being invalid through its
|
||||||
// default argument.
|
// default argument.
|
||||||
Kind == TypeOfKind::Unqualified && !Can.isNull()
|
Kind == TypeOfKind::Unqualified && !Can.isNull()
|
||||||
? Can.getAtomicUnqualifiedType()
|
? Context.getUnqualifiedArrayType(Can).getAtomicUnqualifiedType()
|
||||||
: Can,
|
: Can,
|
||||||
toTypeDependence(E->getDependence()) |
|
toTypeDependence(E->getDependence()) |
|
||||||
(E->getType()->getDependence() &
|
(E->getType()->getDependence() &
|
||||||
TypeDependence::VariablyModified)),
|
TypeDependence::VariablyModified)),
|
||||||
TOExpr(E) {
|
TOExpr(E), Context(Context) {
|
||||||
TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified;
|
TypeOfBits.Kind = static_cast<unsigned>(Kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeOfExprType::isSugared() const {
|
bool TypeOfExprType::isSugared() const {
|
||||||
@ -3911,7 +3913,9 @@ bool TypeOfExprType::isSugared() const {
|
|||||||
QualType TypeOfExprType::desugar() const {
|
QualType TypeOfExprType::desugar() const {
|
||||||
if (isSugared()) {
|
if (isSugared()) {
|
||||||
QualType QT = getUnderlyingExpr()->getType();
|
QualType QT = getUnderlyingExpr()->getType();
|
||||||
return TypeOfBits.IsUnqual ? QT.getAtomicUnqualifiedType() : QT;
|
return getKind() == TypeOfKind::Unqualified
|
||||||
|
? Context.getUnqualifiedArrayType(QT).getAtomicUnqualifiedType()
|
||||||
|
: QT;
|
||||||
}
|
}
|
||||||
return QualType(this, 0);
|
return QualType(this, 0);
|
||||||
}
|
}
|
||||||
@ -3923,6 +3927,24 @@ void DependentTypeOfExprType::Profile(llvm::FoldingSetNodeID &ID,
|
|||||||
ID.AddBoolean(IsUnqual);
|
ID.AddBoolean(IsUnqual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeOfType::TypeOfType(const ASTContext &Context, QualType T, QualType Can,
|
||||||
|
TypeOfKind Kind)
|
||||||
|
: Type(TypeOf,
|
||||||
|
Kind == TypeOfKind::Unqualified
|
||||||
|
? Context.getUnqualifiedArrayType(Can).getAtomicUnqualifiedType()
|
||||||
|
: Can,
|
||||||
|
T->getDependence()),
|
||||||
|
TOType(T), Context(Context) {
|
||||||
|
TypeOfBits.Kind = static_cast<unsigned>(Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
QualType TypeOfType::desugar() const {
|
||||||
|
QualType QT = getUnmodifiedType();
|
||||||
|
return getKind() == TypeOfKind::Unqualified
|
||||||
|
? Context.getUnqualifiedArrayType(QT).getAtomicUnqualifiedType()
|
||||||
|
: QT;
|
||||||
|
}
|
||||||
|
|
||||||
DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)
|
DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)
|
||||||
// C++11 [temp.type]p2: "If an expression e involves a template parameter,
|
// C++11 [temp.type]p2: "If an expression e involves a template parameter,
|
||||||
// decltype(e) denotes a unique dependent type." Hence a decltype type is
|
// decltype(e) denotes a unique dependent type." Hence a decltype type is
|
||||||
|
@ -92,3 +92,50 @@ extern __attribute__((address_space(0))) int type_attr_test_2; // expec
|
|||||||
void invalid_param_fn(__attribute__((address_space(1))) int i); // expected-error {{parameter may not be qualified with an address space}}
|
void invalid_param_fn(__attribute__((address_space(1))) int i); // expected-error {{parameter may not be qualified with an address space}}
|
||||||
typeof(invalid_param_fn) invalid_param_1;
|
typeof(invalid_param_fn) invalid_param_1;
|
||||||
typeof_unqual(invalid_param_fn) invalid_param_2;
|
typeof_unqual(invalid_param_fn) invalid_param_2;
|
||||||
|
|
||||||
|
// Ensure restrict is stripped
|
||||||
|
extern int *restrict p1;
|
||||||
|
extern int *p2;
|
||||||
|
extern typeof(p1) p1;
|
||||||
|
extern typeof_unqual(p1) p2;
|
||||||
|
|
||||||
|
// Ensure array qualifications are removed
|
||||||
|
extern const int aci[2];
|
||||||
|
extern const int acii[2][2];
|
||||||
|
extern int ai[2];
|
||||||
|
extern int aii[2][2];
|
||||||
|
extern typeof(aci) aci;
|
||||||
|
extern typeof_unqual(aci) ai;
|
||||||
|
extern typeof(acii) acii;
|
||||||
|
extern typeof_unqual(acii) aii;
|
||||||
|
|
||||||
|
extern int *restrict arpi[2];
|
||||||
|
extern int *restrict arpii[2][2];
|
||||||
|
extern int *api[2];
|
||||||
|
extern int *apii[2][2];
|
||||||
|
extern typeof(arpi) arpi;
|
||||||
|
extern typeof_unqual(arpi) api;
|
||||||
|
extern typeof(arpii) arpii;
|
||||||
|
extern typeof_unqual(arpii) apii;
|
||||||
|
|
||||||
|
extern int _Atomic aAi[2];
|
||||||
|
extern int _Atomic aAii[2][2];
|
||||||
|
extern typeof(aAi) aAi;
|
||||||
|
extern typeof_unqual(aAi) aAi;
|
||||||
|
extern typeof(aAii) aAii;
|
||||||
|
extern typeof_unqual(aAii) aAii;
|
||||||
|
|
||||||
|
extern _Atomic(int) aAi[2];
|
||||||
|
extern _Atomic(int) aAii[2][2];
|
||||||
|
extern typeof(aAi) aAi;
|
||||||
|
extern typeof_unqual(aAi) aAi;
|
||||||
|
extern typeof(aAii) aAii;
|
||||||
|
extern typeof_unqual(aAii) aAii;
|
||||||
|
|
||||||
|
const char* const animals[] = { "aardvark", "bluejay", "catte" };
|
||||||
|
void GH92667(void) {
|
||||||
|
const char* animals2_array1[3];
|
||||||
|
typeof_unqual(animals) animals2_array;
|
||||||
|
animals2_array1[0] = 0;
|
||||||
|
animals2_array[0] = 0;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user