Skip to content

[Clang] [C23] Fix typeof_unqual for qualified array types #92767

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,8 @@ Bug Fixes in This Version
- ``__has_unique_object_representations`` correctly handles arrays of unknown bounds of
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2653,6 +2653,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// \returns if this is an array type, the completely unqualified array type
/// that corresponds to it. Otherwise, returns T.getUnqualifiedType().
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
/// cvr-qualifiers have been removed.
Expand Down
37 changes: 17 additions & 20 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1618,6 +1618,10 @@ class QualType {
QualType stripObjCKindOfType(const ASTContext &ctx) const;

/// 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;

private:
Expand Down Expand Up @@ -2105,8 +2109,8 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {

LLVM_PREFERRED_TYPE(TypeBitfields)
unsigned : NumTypeBits;
LLVM_PREFERRED_TYPE(bool)
unsigned IsUnqual : 1; // If true: typeof_unqual, else: typeof
LLVM_PREFERRED_TYPE(TypeOfKind)
unsigned Kind : 1;
};

class UsingBitfields {
Expand Down Expand Up @@ -5661,19 +5665,20 @@ class MacroQualifiedType : public Type {
/// extension) or a `typeof_unqual` expression (a C23 feature).
class TypeOfExprType : public Type {
Expr *TOExpr;
const ASTContext &Context;

protected:
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:
Expr *getUnderlyingExpr() const { return TOExpr; }

/// Returns the kind of 'typeof' type this is.
TypeOfKind getKind() const {
return TypeOfBits.IsUnqual ? TypeOfKind::Unqualified
: TypeOfKind::Qualified;
return static_cast<TypeOfKind>(TypeOfBits.Kind);
}

/// Remove a single level of sugar.
Expand All @@ -5694,7 +5699,8 @@ class TypeOfExprType : public Type {
class DependentTypeOfExprType : public TypeOfExprType,
public llvm::FoldingSetNode {
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) {
Profile(ID, Context, getUnderlyingExpr(),
Expand All @@ -5711,32 +5717,23 @@ class TypeOfType : public Type {
friend class ASTContext; // ASTContext creates these.

QualType TOType;
const ASTContext &Context;

TypeOfType(QualType T, QualType Can, TypeOfKind Kind)
: Type(TypeOf,
Kind == TypeOfKind::Unqualified ? Can.getAtomicUnqualifiedType()
: Can,
T->getDependence()),
TOType(T) {
TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified;
}
TypeOfType(const ASTContext &Context, QualType T, QualType Can,
TypeOfKind Kind);

public:
QualType getUnmodifiedType() const { return TOType; }

/// Remove a single level of sugar.
QualType desugar() const {
QualType QT = getUnmodifiedType();
return TypeOfBits.IsUnqual ? QT.getAtomicUnqualifiedType() : QT;
}
QualType desugar() const;

/// Returns whether this type directly provides sugar.
bool isSugared() const { return true; }

/// Returns the kind of 'typeof' type this is.
TypeOfKind getKind() const {
return TypeOfBits.IsUnqual ? TypeOfKind::Unqualified
: TypeOfKind::Qualified;
return static_cast<TypeOfKind>(TypeOfBits.Kind);
}

static bool classof(const Type *T) { return T->getTypeClass() == TypeOf; }
Expand Down
12 changes: 6 additions & 6 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6020,19 +6020,19 @@ QualType ASTContext::getTypeOfExprType(Expr *tofExpr, TypeOfKind Kind) const {
if (Canon) {
// We already have a "canonical" version of an identical, dependent
// typeof(expr) type. Use that as our canonical type.
toe = new (*this, alignof(TypeOfExprType))
TypeOfExprType(tofExpr, Kind, QualType((TypeOfExprType *)Canon, 0));
toe = new (*this, alignof(TypeOfExprType)) TypeOfExprType(
*this, tofExpr, Kind, QualType((TypeOfExprType *)Canon, 0));
} else {
// Build a new, canonical typeof(expr) type.
Canon = new (*this, alignof(DependentTypeOfExprType))
DependentTypeOfExprType(tofExpr, Kind);
DependentTypeOfExprType(*this, tofExpr, Kind);
DependentTypeOfExprTypes.InsertNode(Canon, InsertPos);
toe = Canon;
}
} else {
QualType Canonical = getCanonicalType(tofExpr->getType());
toe = new (*this, alignof(TypeOfExprType))
TypeOfExprType(tofExpr, Kind, Canonical);
TypeOfExprType(*this, tofExpr, Kind, Canonical);
}
Types.push_back(toe);
return QualType(toe, 0);
Expand All @@ -6045,8 +6045,8 @@ QualType ASTContext::getTypeOfExprType(Expr *tofExpr, TypeOfKind Kind) const {
/// on canonical types (which are always unique).
QualType ASTContext::getTypeOfType(QualType tofType, TypeOfKind Kind) const {
QualType Canonical = getCanonicalType(tofType);
auto *tot =
new (*this, alignof(TypeOfType)) TypeOfType(tofType, Canonical, Kind);
auto *tot = new (*this, alignof(TypeOfType))
TypeOfType(*this, tofType, Canonical, Kind);
Types.push_back(tot);
return QualType(tot, 0);
}
Expand Down
38 changes: 30 additions & 8 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1627,9 +1627,10 @@ QualType QualType::stripObjCKindOfType(const ASTContext &constCtx) const {
}

QualType QualType::getAtomicUnqualifiedType() const {
if (const auto AT = getTypePtr()->getAs<AtomicType>())
return AT->getValueType().getUnqualifiedType();
return getUnqualifiedType();
QualType T = *this;
if (const auto AT = T.getTypePtr()->getAs<AtomicType>())
T = AT->getValueType();
return T.getUnqualifiedType();
}

std::optional<ArrayRef<QualType>>
Expand Down Expand Up @@ -3890,18 +3891,19 @@ QualType MacroQualifiedType::getModifiedType() const {
return Inner;
}

TypeOfExprType::TypeOfExprType(Expr *E, TypeOfKind Kind, QualType Can)
TypeOfExprType::TypeOfExprType(const ASTContext &Context, Expr *E,
TypeOfKind Kind, QualType Can)
: Type(TypeOfExpr,
// We have to protect against 'Can' being invalid through its
// default argument.
Kind == TypeOfKind::Unqualified && !Can.isNull()
? Can.getAtomicUnqualifiedType()
? Context.getUnqualifiedArrayType(Can).getAtomicUnqualifiedType()
: Can,
toTypeDependence(E->getDependence()) |
(E->getType()->getDependence() &
TypeDependence::VariablyModified)),
TOExpr(E) {
TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified;
TOExpr(E), Context(Context) {
TypeOfBits.Kind = static_cast<unsigned>(Kind);
}

bool TypeOfExprType::isSugared() const {
Expand All @@ -3911,7 +3913,9 @@ bool TypeOfExprType::isSugared() const {
QualType TypeOfExprType::desugar() const {
if (isSugared()) {
QualType QT = getUnderlyingExpr()->getType();
return TypeOfBits.IsUnqual ? QT.getAtomicUnqualifiedType() : QT;
return getKind() == TypeOfKind::Unqualified
? Context.getUnqualifiedArrayType(QT).getAtomicUnqualifiedType()
: QT;
}
return QualType(this, 0);
}
Expand All @@ -3923,6 +3927,24 @@ void DependentTypeOfExprType::Profile(llvm::FoldingSetNodeID &ID,
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)
// 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
Expand Down
47 changes: 47 additions & 0 deletions clang/test/Sema/c2x-typeof.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
typeof(invalid_param_fn) invalid_param_1;
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;
}