[C2x] implement typeof and typeof_unqual

This implements WG14 N2927 and WG14 N2930, which together define the
feature for typeof and typeof_unqual, which get the type of their
argument as either fully qualified or fully unqualified. The argument
to either operator is either a type name or an expression. If given a
type name, the type information is pulled directly from the given name.
If given an expression, the type information is pulled from the
expression. Recursive use of these operators is allowed and has the
expected behavior (the innermost operator is resolved to a type, and
that's used to resolve the next layer of typeof specifier, until a
fully resolved type is determined.

Note, we already supported typeof in GNU mode as a non-conforming
extension and we are *not* exposing typeof_unqual as a non-conforming
extension in that mode, nor are we exposing typeof or typeof_unqual as
a nonconforming extension in other language modes. The GNU variant of
typeof supports a form where the parentheses are elided from the
operator when given an expression (e.g., typeof 0 i = 12;). When in C2x
mode, we do not support this extension.

Differential Revision: https://reviews.llvm.org/D134286
This commit is contained in:
Aaron Ballman 2022-09-28 13:25:58 -04:00
parent 153eeb4a5e
commit 60727d8569
42 changed files with 551 additions and 118 deletions

View File

@ -311,6 +311,22 @@ C2x Feature Support
``-Wunused-label`` warning.
- Implemented `WG14 N2508 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2508.pdf>`_,
so labels can placed everywhere inside a compound statement.
- Implemented `WG14 N2927 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2927.htm>`_,
the Not-so-magic ``typeof`` operator. Also implemented
`WG14 N2930 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2930.pdf>`_,
renaming ``remove_quals``, so the ``typeof_unqual`` operator is also
supported. Both of these operators are supported only in C2x mode. The
``typeof`` operator specifies the type of the given parenthesized expression
operand or type name, including all qualifiers. The ``typeof_unqual``
operator is similar to ``typeof`` except that all qualifiers are removed,
including atomic type qualification and type attributes which behave like a
qualifier, such as an address space attribute.
.. code-block:: c
__attribute__((address_space(1))) const _Atomic int Val;
typeof(Val) OtherVal; // type is '__attribute__((address_space(1))) const _Atomic int'
typeof_unqual(Val) OtherValUnqual; // type is 'int'
C++ Language Changes in Clang
-----------------------------

View File

@ -1714,9 +1714,9 @@ public:
/// Return a ObjCObjectPointerType type for the given ObjCObjectType.
QualType getObjCObjectPointerType(QualType OIT) const;
/// GCC extension.
QualType getTypeOfExprType(Expr *e) const;
QualType getTypeOfType(QualType t) const;
/// C2x feature and GCC extension.
QualType getTypeOfExprType(Expr *E, TypeOfKind Kind) const;
QualType getTypeOfType(QualType QT, TypeOfKind Kind) const;
QualType getReferenceQualifiedType(const Expr *e) const;

View File

@ -529,7 +529,7 @@ struct CanProxyAdaptor<FunctionProtoType>
template<>
struct CanProxyAdaptor<TypeOfType> : public CanProxyBase<TypeOfType> {
LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getUnderlyingType)
LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getUnmodifiedType)
};
template<>

View File

@ -138,6 +138,7 @@ def TemplateArgument : PropertyType;
def TemplateArgumentKind : EnumPropertyType<"TemplateArgument::ArgKind">;
def TemplateName : DefaultValuePropertyType;
def TemplateNameKind : EnumPropertyType<"TemplateName::NameKind">;
def TypeOfKind : EnumPropertyType<"TypeOfKind">;
def UInt32 : CountPropertyType<"uint32_t">;
def UInt64 : CountPropertyType<"uint64_t">;
def UnaryTypeTransformKind : EnumPropertyType<"UnaryTransformType::UTTKind">;

View File

@ -1070,7 +1070,7 @@ DEF_TRAVERSE_TYPE(TypedefType, {})
DEF_TRAVERSE_TYPE(TypeOfExprType,
{ TRY_TO(TraverseStmt(T->getUnderlyingExpr())); })
DEF_TRAVERSE_TYPE(TypeOfType, { TRY_TO(TraverseType(T->getUnderlyingType())); })
DEF_TRAVERSE_TYPE(TypeOfType, { TRY_TO(TraverseType(T->getUnmodifiedType())); })
DEF_TRAVERSE_TYPE(DecltypeType,
{ TRY_TO(TraverseStmt(T->getUnderlyingExpr())); })
@ -1345,7 +1345,7 @@ DEF_TRAVERSE_TYPELOC(TypeOfExprType,
{ TRY_TO(TraverseStmt(TL.getUnderlyingExpr())); })
DEF_TRAVERSE_TYPELOC(TypeOfType, {
TRY_TO(TraverseTypeLoc(TL.getUnderlyingTInfo()->getTypeLoc()));
TRY_TO(TraverseTypeLoc(TL.getUnmodifiedTInfo()->getTypeLoc()));
})
// FIXME: location of underlying expr

View File

@ -714,6 +714,12 @@ enum class ObjCSubstitutionContext {
Superclass,
};
/// The kind of 'typeof' expression we're after.
enum class TypeOfKind : uint8_t {
Qualified,
Unqualified,
};
/// A (possibly-)qualified type.
///
/// For efficiency, we don't store CV-qualified types as nodes on their
@ -1793,6 +1799,14 @@ protected:
unsigned NumArgs;
};
class TypeOfBitfields {
friend class TypeOfType;
friend class TypeOfExprType;
unsigned : NumTypeBits;
unsigned IsUnqual : 1; // If true: typeof_unqual, else: typeof
};
class SubstTemplateTypeParmTypeBitfields {
friend class SubstTemplateTypeParmType;
@ -1882,6 +1896,7 @@ protected:
ConstantArrayTypeBitfields ConstantArrayTypeBits;
AttributedTypeBitfields AttributedTypeBits;
AutoTypeBitfields AutoTypeBits;
TypeOfBitfields TypeOfBits;
BuiltinTypeBitfields BuiltinTypeBits;
FunctionTypeBitfields FunctionTypeBits;
ObjCObjectTypeBitfields ObjCObjectTypeBits;
@ -4532,18 +4547,22 @@ public:
}
};
/// Represents a `typeof` (or __typeof__) expression (a GCC extension).
/// Represents a `typeof` (or __typeof__) expression (a C2x feature and GCC
/// extension) or a `typeof_unqual` expression (a C2x feature).
class TypeOfExprType : public Type {
Expr *TOExpr;
protected:
friend class ASTContext; // ASTContext creates these.
TypeOfExprType(Expr *E, QualType can = QualType());
TypeOfExprType(Expr *E, TypeOfKind Kind, QualType Can = QualType());
public:
Expr *getUnderlyingExpr() const { return TOExpr; }
/// Returns true if this is a typeof_unqual type.
bool isUnqual() const { return TypeOfBits.IsUnqual; }
/// Remove a single level of sugar.
QualType desugar() const;
@ -4564,37 +4583,48 @@ class DependentTypeOfExprType
const ASTContext &Context;
public:
DependentTypeOfExprType(const ASTContext &Context, Expr *E)
: TypeOfExprType(E), Context(Context) {}
DependentTypeOfExprType(const ASTContext &Context, Expr *E, TypeOfKind Kind)
: TypeOfExprType(E, Kind), Context(Context) {}
void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, Context, getUnderlyingExpr());
Profile(ID, Context, getUnderlyingExpr(), isUnqual());
}
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
Expr *E);
Expr *E, bool IsUnqual);
};
/// Represents `typeof(type)`, a GCC extension.
/// Represents `typeof(type)`, a C2x feature and GCC extension, or
/// `typeof_unqual(type), a C2x feature.
class TypeOfType : public Type {
friend class ASTContext; // ASTContext creates these.
QualType TOType;
TypeOfType(QualType T, QualType can)
: Type(TypeOf, can, T->getDependence()), TOType(T) {
assert(!isa<TypedefType>(can) && "Invalid canonical type");
TypeOfType(QualType T, QualType Can, TypeOfKind Kind)
: Type(TypeOf,
Kind == TypeOfKind::Unqualified ? Can.getAtomicUnqualifiedType()
: Can,
T->getDependence()),
TOType(T) {
TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified;
}
public:
QualType getUnderlyingType() const { return TOType; }
QualType getUnmodifiedType() const { return TOType; }
/// Remove a single level of sugar.
QualType desugar() const { return getUnderlyingType(); }
QualType desugar() const {
QualType QT = getUnmodifiedType();
return isUnqual() ? QT.getAtomicUnqualifiedType() : QT;
}
/// Returns whether this type directly provides sugar.
bool isSugared() const { return true; }
/// Returns true if this is a typeof_unqual type.
bool isUnqual() const { return TypeOfBits.IsUnqual; }
static bool classof(const Type *T) { return T->getTypeClass() == TypeOf; }
};

View File

@ -1934,7 +1934,7 @@ struct TypeOfExprTypeLocInfo : public TypeofLocInfo {
};
struct TypeOfTypeLocInfo : public TypeofLocInfo {
TypeSourceInfo* UnderlyingTInfo;
TypeSourceInfo *UnmodifiedTInfo;
};
template <class Derived, class TypeClass, class LocalData = TypeofLocInfo>
@ -2002,16 +2002,16 @@ public:
class TypeOfTypeLoc
: public TypeofLikeTypeLoc<TypeOfTypeLoc, TypeOfType, TypeOfTypeLocInfo> {
public:
QualType getUnderlyingType() const {
return this->getTypePtr()->getUnderlyingType();
QualType getUnmodifiedType() const {
return this->getTypePtr()->getUnmodifiedType();
}
TypeSourceInfo* getUnderlyingTInfo() const {
return this->getLocalData()->UnderlyingTInfo;
TypeSourceInfo *getUnmodifiedTInfo() const {
return this->getLocalData()->UnmodifiedTInfo;
}
void setUnderlyingTInfo(TypeSourceInfo* TI) const {
this->getLocalData()->UnderlyingTInfo = TI;
void setUnmodifiedTInfo(TypeSourceInfo *TI) const {
this->getLocalData()->UnmodifiedTInfo = TI;
}
void initializeLocal(ASTContext &Context, SourceLocation Loc);

View File

@ -397,18 +397,28 @@ let Class = TypeOfExprType in {
let Read = [{ node->getUnderlyingExpr() }];
}
def : Property<"kind", TypeOfKind> {
let Read = [{ node->isUnqual() ? TypeOfKind::Unqualified
: TypeOfKind::Qualified }];
}
def : Creator<[{
return ctx.getTypeOfExprType(expression);
return ctx.getTypeOfExprType(expression, kind);
}]>;
}
let Class = TypeOfType in {
def : Property<"underlyingType", QualType> {
let Read = [{ node->getUnderlyingType() }];
def : Property<"unmodifiedType", QualType> {
let Read = [{ node->getUnmodifiedType() }];
}
def : Property<"kind", TypeOfKind> {
let Read = [{ node->isUnqual() ? TypeOfKind::Unqualified
: TypeOfKind::Qualified }];
}
def : Creator<[{
return ctx.getTypeOfType(underlyingType);
return ctx.getTypeOfType(unmodifiedType, kind);
}]>;
}

View File

@ -371,6 +371,9 @@ def warn_cxx11_compat_decltype_auto_type_specifier : Warning<
def ext_auto_type : Extension<
"'__auto_type' is a GNU extension">,
InGroup<GNUAutoType>;
def warn_c2x_compat_typeof_type_specifier : Warning<
"'%select{typeof|typeof_unqual}0' is incompatible with C standards before "
"C2x">, InGroup<CPre2xCompat>, DefaultIgnore;
def ext_for_range : ExtWarn<
"range-based for loop is a C++11 extension">, InGroup<CXX11>;
def warn_cxx98_compat_for_range : Warning<

View File

@ -6530,7 +6530,8 @@ def err_sizeof_alignof_function_type : Error<
def err_openmp_default_simd_align_expr : Error<
"invalid application of '__builtin_omp_required_simd_align' to an expression, only type is allowed">;
def err_sizeof_alignof_typeof_bitfield : Error<
"invalid application of '%select{sizeof|alignof|typeof}0' to bit-field">;
"invalid application of '%select{sizeof|alignof|typeof|typeof_unqual}0' to "
"bit-field">;
def err_alignof_member_of_incomplete_type : Error<
"invalid application of 'alignof' to a field of a class still being defined">;
def err_vecstep_non_scalar_vector_type : Error<

View File

@ -79,8 +79,10 @@ namespace clang {
TST_class, // C++ class type
TST_interface, // C++ (Microsoft-specific) __interface type
TST_typename, // Typedef, C++ class-name or enum name, etc.
TST_typeofType,
TST_typeofExpr,
TST_typeofType, // C2x (and GNU extension) typeof(type-name)
TST_typeofExpr, // C2x (and GNU extension) typeof(expression)
TST_typeof_unqualType, // C2x typeof_unqual(type-name)
TST_typeof_unqualExpr, // C2x typeof_unqual(expression)
TST_decltype, // C++11 decltype
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) TST_##Trait,
#include "clang/Basic/TransformTypeTraits.def"

View File

@ -31,6 +31,9 @@
#ifndef C99_KEYWORD
#define C99_KEYWORD(X,Y) KEYWORD(X,KEYC99|(Y))
#endif
#ifndef C2X_KEYWORD
#define C2X_KEYWORD(X,Y) KEYWORD(X,KEYC2X|(Y))
#endif
#ifndef COROUTINES_KEYWORD
#define COROUTINES_KEYWORD(X) CXX20_KEYWORD(X,KEYCOROUTINES)
#endif
@ -412,6 +415,10 @@ KEYWORD(char8_t , CHAR8SUPPORT)
// C11 Extension
KEYWORD(_Float16 , KEYALL)
// C2x keywords
C2X_KEYWORD(typeof , KEYGNU)
C2X_KEYWORD(typeof_unqual , 0)
// ISO/IEC JTC1 SC22 WG14 N1169 Extension
KEYWORD(_Accum , KEYNOCXX)
KEYWORD(_Fract , KEYNOCXX)
@ -450,9 +457,6 @@ KEYWORD(__FUNCTION__ , KEYALL)
KEYWORD(__PRETTY_FUNCTION__ , KEYALL)
KEYWORD(__auto_type , KEYALL)
// GNU Extensions (outside impl-reserved namespace)
KEYWORD(typeof , KEYGNU|KEYC2X)
// MS Extensions
KEYWORD(__FUNCDNAME__ , KEYMS)
KEYWORD(__FUNCSIG__ , KEYMS)
@ -947,3 +951,4 @@ ANNOTATION(header_unit)
#undef PUNCTUATOR
#undef TOK
#undef C99_KEYWORD
#undef C2X_KEYWORD

View File

@ -289,6 +289,8 @@ public:
static const TST TST_typename = clang::TST_typename;
static const TST TST_typeofType = clang::TST_typeofType;
static const TST TST_typeofExpr = clang::TST_typeofExpr;
static const TST TST_typeof_unqualType = clang::TST_typeof_unqualType;
static const TST TST_typeof_unqualExpr = clang::TST_typeof_unqualExpr;
static const TST TST_decltype = clang::TST_decltype;
static const TST TST_decltype_auto = clang::TST_decltype_auto;
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \
@ -409,10 +411,11 @@ private:
static bool isTypeRep(TST T) {
return T == TST_atomic || T == TST_typename || T == TST_typeofType ||
isTransformTypeTrait(T);
T == TST_typeof_unqualType || isTransformTypeTrait(T);
}
static bool isExprRep(TST T) {
return (T == TST_typeofExpr || T == TST_decltype || T == TST_bitint);
return T == TST_typeofExpr || T == TST_typeof_unqualExpr ||
T == TST_decltype || T == TST_bitint;
}
static bool isTemplateIdRep(TST T) {
return (T == TST_auto || T == TST_decltype_auto);

View File

@ -2523,7 +2523,7 @@ public:
// Returns the underlying type of a decltype with the given expression.
QualType getDecltypeForExpr(Expr *E);
QualType BuildTypeofExprType(Expr *E);
QualType BuildTypeofExprType(Expr *E, TypeOfKind Kind);
/// If AsUnevaluated is false, E is treated as though it were an evaluated
/// context, such as when building a type for decltype(auto).
QualType BuildDecltypeType(Expr *E, bool AsUnevaluated = true);

View File

@ -5581,30 +5581,31 @@ QualType ASTContext::getObjCInterfaceType(const ObjCInterfaceDecl *Decl,
/// multiple declarations that refer to "typeof(x)" all contain different
/// DeclRefExpr's. This doesn't effect the type checker, since it operates
/// on canonical type's (which are always unique).
QualType ASTContext::getTypeOfExprType(Expr *tofExpr) const {
QualType ASTContext::getTypeOfExprType(Expr *tofExpr, TypeOfKind Kind) const {
TypeOfExprType *toe;
if (tofExpr->isTypeDependent()) {
llvm::FoldingSetNodeID ID;
DependentTypeOfExprType::Profile(ID, *this, tofExpr);
DependentTypeOfExprType::Profile(ID, *this, tofExpr,
Kind == TypeOfKind::Unqualified);
void *InsertPos = nullptr;
DependentTypeOfExprType *Canon
= DependentTypeOfExprTypes.FindNodeOrInsertPos(ID, InsertPos);
DependentTypeOfExprType *Canon =
DependentTypeOfExprTypes.FindNodeOrInsertPos(ID, InsertPos);
if (Canon) {
// We already have a "canonical" version of an identical, dependent
// typeof(expr) type. Use that as our canonical type.
toe = new (*this, TypeAlignment) TypeOfExprType(tofExpr,
QualType((TypeOfExprType*)Canon, 0));
toe = new (*this, TypeAlignment)
TypeOfExprType(tofExpr, Kind, QualType((TypeOfExprType *)Canon, 0));
} else {
// Build a new, canonical typeof(expr) type.
Canon
= new (*this, TypeAlignment) DependentTypeOfExprType(*this, tofExpr);
Canon = new (*this, TypeAlignment)
DependentTypeOfExprType(*this, tofExpr, Kind);
DependentTypeOfExprTypes.InsertNode(Canon, InsertPos);
toe = Canon;
}
} else {
QualType Canonical = getCanonicalType(tofExpr->getType());
toe = new (*this, TypeAlignment) TypeOfExprType(tofExpr, Canonical);
toe = new (*this, TypeAlignment) TypeOfExprType(tofExpr, Kind, Canonical);
}
Types.push_back(toe);
return QualType(toe, 0);
@ -5615,9 +5616,10 @@ QualType ASTContext::getTypeOfExprType(Expr *tofExpr) const {
/// memory savings. Since typeof(t) is fairly uncommon, space shouldn't be
/// an issue. This doesn't affect the type checker, since it operates
/// on canonical types (which are always unique).
QualType ASTContext::getTypeOfType(QualType tofType) const {
QualType ASTContext::getTypeOfType(QualType tofType, TypeOfKind Kind) const {
QualType Canonical = getCanonicalType(tofType);
auto *tot = new (*this, TypeAlignment) TypeOfType(tofType, Canonical);
auto *tot =
new (*this, TypeAlignment) TypeOfType(tofType, Canonical, Kind);
Types.push_back(tot);
return QualType(tot, 0);
}
@ -12936,7 +12938,15 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
return Ctx.getTypedefType(CD, Ctx.getQualifiedType(Underlying));
}
case Type::TypeOf:
return Ctx.getTypeOfType(Ctx.getQualifiedType(Underlying));
// The common sugar between two typeof expressions, where one is
// potentially a typeof_unqual and the other is not, we unify to the
// qualified type as that retains the most information along with the type.
// We only return a typeof_unqual type when both types are unqual types.
return Ctx.getTypeOfType(Ctx.getQualifiedType(Underlying),
cast<TypeOfType>(X)->isUnqual() &&
cast<TypeOfType>(Y)->isUnqual()
? TypeOfKind::Unqualified
: TypeOfKind::Qualified);
case Type::TypeOfExpr:
return QualType();

View File

@ -1370,16 +1370,17 @@ ExpectedType ASTNodeImporter::VisitTypeOfExprType(const TypeOfExprType *T) {
ExpectedExpr ToExprOrErr = import(T->getUnderlyingExpr());
if (!ToExprOrErr)
return ToExprOrErr.takeError();
return Importer.getToContext().getTypeOfExprType(*ToExprOrErr);
return Importer.getToContext().getTypeOfExprType(
*ToExprOrErr,
T->isUnqual() ? TypeOfKind::Unqualified : TypeOfKind::Qualified);
}
ExpectedType ASTNodeImporter::VisitTypeOfType(const TypeOfType *T) {
ExpectedType ToUnderlyingTypeOrErr = import(T->getUnderlyingType());
ExpectedType ToUnderlyingTypeOrErr = import(T->getUnmodifiedType());
if (!ToUnderlyingTypeOrErr)
return ToUnderlyingTypeOrErr.takeError();
return Importer.getToContext().getTypeOfType(*ToUnderlyingTypeOrErr);
return Importer.getToContext().getTypeOfType(*ToUnderlyingTypeOrErr,
T->isUnqual() ? TypeOfKind::Unqualified : TypeOfKind::Qualified);
}
ExpectedType ASTNodeImporter::VisitUsingType(const UsingType *T) {

View File

@ -974,8 +974,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
case Type::TypeOf:
if (!IsStructurallyEquivalent(Context,
cast<TypeOfType>(T1)->getUnderlyingType(),
cast<TypeOfType>(T2)->getUnderlyingType()))
cast<TypeOfType>(T1)->getUnmodifiedType(),
cast<TypeOfType>(T2)->getUnmodifiedType()))
return false;
break;

View File

@ -1061,7 +1061,7 @@ public:
VisitType(T);
}
void VisitTypeOfType(const TypeOfType *T) {
AddQualType(T->getUnderlyingType());
AddQualType(T->getUnmodifiedType());
VisitType(T);
}

View File

@ -3469,27 +3469,37 @@ QualType MacroQualifiedType::getModifiedType() const {
return Inner;
}
TypeOfExprType::TypeOfExprType(Expr *E, QualType can)
: Type(TypeOfExpr, can,
TypeOfExprType::TypeOfExprType(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()
: Can,
toTypeDependence(E->getDependence()) |
(E->getType()->getDependence() &
TypeDependence::VariablyModified)),
TOExpr(E) {}
TOExpr(E) {
TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified;
}
bool TypeOfExprType::isSugared() const {
return !TOExpr->isTypeDependent();
}
QualType TypeOfExprType::desugar() const {
if (isSugared())
return getUnderlyingExpr()->getType();
if (isSugared()) {
QualType QT = getUnderlyingExpr()->getType();
return isUnqual() ? QT.getAtomicUnqualifiedType() : QT;
}
return QualType(this, 0);
}
void DependentTypeOfExprType::Profile(llvm::FoldingSetNodeID &ID,
const ASTContext &Context, Expr *E) {
const ASTContext &Context, Expr *E,
bool IsUnqual) {
E->Profile(ID, Context, true);
ID.AddBoolean(IsUnqual);
}
DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)

View File

@ -521,8 +521,8 @@ void TypeOfTypeLoc::initializeLocal(ASTContext &Context,
SourceLocation Loc) {
TypeofLikeTypeLoc<TypeOfTypeLoc, TypeOfType, TypeOfTypeLocInfo>
::initializeLocal(Context, Loc);
this->getLocalData()->UnderlyingTInfo = Context.getTrivialTypeSourceInfo(
getUnderlyingType(), Loc);
this->getLocalData()->UnmodifiedTInfo =
Context.getTrivialTypeSourceInfo(getUnmodifiedType(), Loc);
}
void UnaryTransformTypeLoc::initializeLocal(ASTContext &Context,

View File

@ -1110,7 +1110,7 @@ void TypePrinter::printTypedefAfter(const TypedefType *T, raw_ostream &OS) {}
void TypePrinter::printTypeOfExprBefore(const TypeOfExprType *T,
raw_ostream &OS) {
OS << "typeof ";
OS << (T->isUnqual() ? "typeof_unqual " : "typeof ");
if (T->getUnderlyingExpr())
T->getUnderlyingExpr()->printPretty(OS, nullptr, Policy);
spaceBeforePlaceHolder(OS);
@ -1120,8 +1120,8 @@ void TypePrinter::printTypeOfExprAfter(const TypeOfExprType *T,
raw_ostream &OS) {}
void TypePrinter::printTypeOfBefore(const TypeOfType *T, raw_ostream &OS) {
OS << "typeof(";
print(T->getUnderlyingType(), OS, StringRef());
OS << (T->isUnqual() ? "typeof_unqual(" : "typeof(");
print(T->getUnmodifiedType(), OS, StringRef());
OS << ')';
spaceBeforePlaceHolder(OS);
}

View File

@ -3287,7 +3287,7 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
T = cast<TypeOfExprType>(T)->getUnderlyingExpr()->getType();
break;
case Type::TypeOf:
T = cast<TypeOfType>(T)->getUnderlyingType();
T = cast<TypeOfType>(T)->getUnmodifiedType();
break;
case Type::Decltype:
T = cast<DecltypeType>(T)->getUnderlyingType();

View File

@ -4241,8 +4241,9 @@ void Parser::ParseDeclarationSpecifiers(
continue;
break;
// GNU typeof support.
// C2x/GNU typeof support.
case tok::kw_typeof:
case tok::kw_typeof_unqual:
ParseTypeofSpecifier(DS);
continue;
@ -5252,8 +5253,9 @@ bool Parser::isTypeSpecifierQualifier() {
// GNU attributes support.
case tok::kw___attribute:
// GNU typeof support.
// C2x/GNU typeof support.
case tok::kw_typeof:
case tok::kw_typeof_unqual:
// type-specifiers
case tok::kw_short:
@ -5495,8 +5497,9 @@ bool Parser::isDeclarationSpecifier(
case tok::kw_static_assert:
case tok::kw__Static_assert:
// GNU typeof support.
// C2x/GNU typeof support.
case tok::kw_typeof:
case tok::kw_typeof_unqual:
// GNU attributes.
case tok::kw___attribute:
@ -7543,13 +7546,27 @@ void Parser::ParseMisplacedBracketDeclarator(Declarator &D) {
/// typeof ( expressions )
/// typeof ( type-name )
/// [GNU/C++] typeof unary-expression
/// [C2x] typeof-specifier:
/// typeof '(' typeof-specifier-argument ')'
/// typeof_unqual '(' typeof-specifier-argument ')'
///
/// typeof-specifier-argument:
/// expression
/// type-name
///
void Parser::ParseTypeofSpecifier(DeclSpec &DS) {
assert(Tok.is(tok::kw_typeof) && "Not a typeof specifier");
assert(Tok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual) &&
"Not a typeof specifier");
bool IsUnqual = Tok.is(tok::kw_typeof_unqual);
const IdentifierInfo *II = Tok.getIdentifierInfo();
if (getLangOpts().C2x && !II->getName().startswith("__"))
Diag(Tok.getLocation(), diag::warn_c2x_compat_typeof_type_specifier)
<< IsUnqual;
Token OpTok = Tok;
SourceLocation StartLoc = ConsumeToken();
const bool hasParens = Tok.is(tok::l_paren);
bool HasParens = Tok.is(tok::l_paren);
EnterExpressionEvaluationContext Unevaluated(
Actions, Sema::ExpressionEvaluationContext::Unevaluated,
@ -7560,7 +7577,7 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) {
SourceRange CastRange;
ExprResult Operand = Actions.CorrectDelayedTyposInExpr(
ParseExprAfterUnaryExprOrTypeTrait(OpTok, isCastExpr, CastTy, CastRange));
if (hasParens)
if (HasParens)
DS.setTypeArgumentRange(CastRange);
if (CastRange.getEnd().isInvalid())
@ -7578,7 +7595,9 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) {
const char *PrevSpec = nullptr;
unsigned DiagID;
// Check for duplicate type specifiers (e.g. "int typeof(int)").
if (DS.SetTypeSpecType(DeclSpec::TST_typeofType, StartLoc, PrevSpec,
if (DS.SetTypeSpecType(IsUnqual ? DeclSpec::TST_typeof_unqualType
: DeclSpec::TST_typeofType,
StartLoc, PrevSpec,
DiagID, CastTy,
Actions.getASTContext().getPrintingPolicy()))
Diag(StartLoc, DiagID) << PrevSpec;
@ -7601,7 +7620,9 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) {
const char *PrevSpec = nullptr;
unsigned DiagID;
// Check for duplicate type specifiers (e.g. "int typeof(int)").
if (DS.SetTypeSpecType(DeclSpec::TST_typeofExpr, StartLoc, PrevSpec,
if (DS.SetTypeSpecType(IsUnqual ? DeclSpec::TST_typeof_unqualExpr
: DeclSpec::TST_typeofExpr,
StartLoc, PrevSpec,
DiagID, Operand.get(),
Actions.getASTContext().getPrintingPolicy()))
Diag(StartLoc, DiagID) << PrevSpec;

View File

@ -2288,6 +2288,13 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
/// typeof ( expressions )
/// typeof ( type-name )
/// [GNU/C++] typeof unary-expression
/// [C2x] typeof-specifier:
/// typeof '(' typeof-specifier-argument ')'
/// typeof_unqual '(' typeof-specifier-argument ')'
///
/// typeof-specifier-argument:
/// expression
/// type-name
///
/// [OpenCL 1.1 6.11.12] vec_step built-in function:
/// vec_step ( expressions )
@ -2299,8 +2306,9 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
ParsedType &CastTy,
SourceRange &CastRange) {
assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_sizeof, tok::kw___alignof,
tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step,
assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual, tok::kw_sizeof,
tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof,
tok::kw_vec_step,
tok::kw___builtin_omp_required_simd_align) &&
"Not a typeof/sizeof/alignof/vec_step expression!");
@ -2336,7 +2344,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
}
isCastExpr = false;
if (OpTok.is(tok::kw_typeof) && !getLangOpts().CPlusPlus) {
if (OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual) &&
!getLangOpts().CPlusPlus) {
Diag(Tok, diag::err_expected_after) << OpTok.getIdentifierInfo()
<< tok::l_paren;
return ExprError();
@ -2362,7 +2371,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
return ExprEmpty();
}
if (getLangOpts().CPlusPlus || OpTok.isNot(tok::kw_typeof)) {
if (getLangOpts().CPlusPlus ||
!OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual)) {
// GNU typeof in C requires the expression to be parenthesized. Not so for
// sizeof/alignof or in C++. Therefore, the parenthesized expression is
// the start of a unary-expression, but doesn't include any postfix

View File

@ -384,6 +384,7 @@ bool Declarator::isDeclarationOfFunction() const {
return false;
case TST_decltype:
case TST_typeof_unqualExpr:
case TST_typeofExpr:
if (Expr *E = DS.getRepAsExpr())
return E->getType()->isFunctionType();
@ -392,6 +393,7 @@ bool Declarator::isDeclarationOfFunction() const {
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case TST_##Trait:
#include "clang/Basic/TransformTypeTraits.def"
case TST_typename:
case TST_typeof_unqualType:
case TST_typeofType: {
QualType QT = DS.getRepAsType().get();
if (QT.isNull())
@ -573,6 +575,8 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T,
case DeclSpec::TST_typename: return "type-name";
case DeclSpec::TST_typeofType:
case DeclSpec::TST_typeofExpr: return "typeof";
case DeclSpec::TST_typeof_unqualType:
case DeclSpec::TST_typeof_unqualExpr: return "typeof_unqual";
case DeclSpec::TST_auto: return "auto";
case DeclSpec::TST_auto_type: return "__auto_type";
case DeclSpec::TST_decltype: return "(decltype)";

View File

@ -5939,6 +5939,7 @@ static bool RebuildDeclaratorInCurrentInstantiation(Sema &S, Declarator &D,
switch (DS.getTypeSpecType()) {
case DeclSpec::TST_typename:
case DeclSpec::TST_typeofType:
case DeclSpec::TST_typeof_unqualType:
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case DeclSpec::TST_##Trait:
#include "clang/Basic/TransformTypeTraits.def"
case DeclSpec::TST_atomic: {
@ -5964,6 +5965,7 @@ static bool RebuildDeclaratorInCurrentInstantiation(Sema &S, Declarator &D,
}
case DeclSpec::TST_decltype:
case DeclSpec::TST_typeof_unqualExpr:
case DeclSpec::TST_typeofExpr: {
Expr *E = DS.getRepAsExpr();
ExprResult Result = S.RebuildExprInCurrentInstantiation(E);

View File

@ -6257,7 +6257,7 @@ bool UnnamedLocalNoLinkageFinder::VisitTypeOfExprType(const TypeOfExprType*) {
}
bool UnnamedLocalNoLinkageFinder::VisitTypeOfType(const TypeOfType* T) {
return Visit(T->getUnderlyingType());
return Visit(T->getUnmodifiedType());
}
bool UnnamedLocalNoLinkageFinder::VisitDecltypeType(const DecltypeType*) {

View File

@ -5987,8 +5987,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
case Type::TypeOf:
if (!OnlyDeduced)
MarkUsedTemplateParameters(Ctx,
cast<TypeOfType>(T)->getUnderlyingType(),
MarkUsedTemplateParameters(Ctx, cast<TypeOfType>(T)->getUnmodifiedType(),
OnlyDeduced, Depth, Used);
break;

View File

@ -863,6 +863,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
const DeclSpec &DS = D.getDeclSpec();
switch (DS.getTypeSpecType()) {
case TST_typename:
case TST_typeof_unqualType:
case TST_typeofType:
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case TST_##Trait:
#include "clang/Basic/TransformTypeTraits.def"
@ -873,6 +874,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
break;
}
case TST_typeof_unqualExpr:
case TST_typeofExpr:
case TST_decltype:
case TST_bitint:

View File

@ -1610,6 +1610,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
// TypeQuals handled by caller.
break;
}
case DeclSpec::TST_typeof_unqualType:
case DeclSpec::TST_typeofType:
// FIXME: Preserve type source info.
Result = S.GetTypeFromParser(DS.getRepAsType());
@ -1618,13 +1619,20 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
if (const TagType *TT = Result->getAs<TagType>())
S.DiagnoseUseOfDecl(TT->getDecl(), DS.getTypeSpecTypeLoc());
// TypeQuals handled by caller.
Result = Context.getTypeOfType(Result);
Result = Context.getTypeOfType(
Result, DS.getTypeSpecType() == DeclSpec::TST_typeof_unqualType
? TypeOfKind::Unqualified
: TypeOfKind::Qualified);
break;
case DeclSpec::TST_typeof_unqualExpr:
case DeclSpec::TST_typeofExpr: {
Expr *E = DS.getRepAsExpr();
assert(E && "Didn't get an expression for typeof?");
// TypeQuals handled by caller.
Result = S.BuildTypeofExprType(E);
Result = S.BuildTypeofExprType(E, DS.getTypeSpecType() ==
DeclSpec::TST_typeof_unqualExpr
? TypeOfKind::Unqualified
: TypeOfKind::Qualified);
if (Result.isNull()) {
Result = Context.IntTy;
declarator.setInvalidType(true);
@ -6103,18 +6111,20 @@ namespace {
}
void VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) {
assert(DS.getTypeSpecType() == DeclSpec::TST_typeofExpr);
assert(DS.getTypeSpecType() == DeclSpec::TST_typeofExpr ||
DS.getTypeSpecType() == DeclSpec::TST_typeof_unqualExpr);
TL.setTypeofLoc(DS.getTypeSpecTypeLoc());
TL.setParensRange(DS.getTypeofParensRange());
}
void VisitTypeOfTypeLoc(TypeOfTypeLoc TL) {
assert(DS.getTypeSpecType() == DeclSpec::TST_typeofType);
assert(DS.getTypeSpecType() == DeclSpec::TST_typeofType ||
DS.getTypeSpecType() == DeclSpec::TST_typeof_unqualType);
TL.setTypeofLoc(DS.getTypeSpecTypeLoc());
TL.setParensRange(DS.getTypeofParensRange());
assert(DS.getRepAsType());
TypeSourceInfo *TInfo = nullptr;
Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo);
TL.setUnderlyingTInfo(TInfo);
TL.setUnmodifiedTInfo(TInfo);
}
void VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
assert(DS.getTypeSpecType() == DeclSpec::TST_decltype);
@ -9163,18 +9173,19 @@ QualType Sema::getElaboratedType(ElaboratedTypeKeyword Keyword,
Keyword, SS.isValid() ? SS.getScopeRep() : nullptr, T, OwnedTagDecl);
}
QualType Sema::BuildTypeofExprType(Expr *E) {
QualType Sema::BuildTypeofExprType(Expr *E, TypeOfKind Kind) {
assert(!E->hasPlaceholderType() && "unexpected placeholder");
if (!getLangOpts().CPlusPlus && E->refersToBitField())
Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 2;
Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield)
<< (Kind == TypeOfKind::Unqualified ? 3 : 2);
if (!E->isTypeDependent()) {
QualType T = E->getType();
if (const TagType *TT = T->getAs<TagType>())
DiagnoseUseOfDecl(TT->getDecl(), E->getExprLoc());
}
return Context.getTypeOfExprType(E);
return Context.getTypeOfExprType(E, Kind);
}
/// getDecltypeForExpr - Given an expr, will return the decltype for

View File

@ -963,12 +963,13 @@ public:
///
/// By default, performs semantic analysis when building the typeof type.
/// Subclasses may override this routine to provide different behavior.
QualType RebuildTypeOfExprType(Expr *Underlying, SourceLocation Loc);
QualType RebuildTypeOfExprType(Expr *Underlying, SourceLocation Loc,
TypeOfKind Kind);
/// Build a new typeof(type) type.
///
/// By default, builds a new TypeOfType with the given underlying type.
QualType RebuildTypeOfType(QualType Underlying);
QualType RebuildTypeOfType(QualType Underlying, TypeOfKind Kind);
/// Build a new unary transform type.
QualType RebuildUnaryTransformType(QualType BaseType,
@ -6199,13 +6200,15 @@ QualType TreeTransform<Derived>::TransformTypeOfExprType(TypeLocBuilder &TLB,
return QualType();
QualType Result = TL.getType();
bool IsUnqual = Result->getAs<TypeOfExprType>()->isUnqual();
if (getDerived().AlwaysRebuild() ||
E.get() != TL.getUnderlyingExpr()) {
Result = getDerived().RebuildTypeOfExprType(E.get(), TL.getTypeofLoc());
Result = getDerived().RebuildTypeOfExprType(
E.get(), TL.getTypeofLoc(),
IsUnqual ? TypeOfKind::Unqualified : TypeOfKind::Qualified);
if (Result.isNull())
return QualType();
}
else E.get();
TypeOfExprTypeLoc NewTL = TLB.push<TypeOfExprTypeLoc>(Result);
NewTL.setTypeofLoc(TL.getTypeofLoc());
@ -6218,14 +6221,17 @@ QualType TreeTransform<Derived>::TransformTypeOfExprType(TypeLocBuilder &TLB,
template<typename Derived>
QualType TreeTransform<Derived>::TransformTypeOfType(TypeLocBuilder &TLB,
TypeOfTypeLoc TL) {
TypeSourceInfo* Old_Under_TI = TL.getUnderlyingTInfo();
TypeSourceInfo* Old_Under_TI = TL.getUnmodifiedTInfo();
TypeSourceInfo* New_Under_TI = getDerived().TransformType(Old_Under_TI);
if (!New_Under_TI)
return QualType();
QualType Result = TL.getType();
bool IsUnqual = Result->getAs<TypeOfType>()->isUnqual();
if (getDerived().AlwaysRebuild() || New_Under_TI != Old_Under_TI) {
Result = getDerived().RebuildTypeOfType(New_Under_TI->getType());
Result = getDerived().RebuildTypeOfType(New_Under_TI->getType(),
IsUnqual ? TypeOfKind::Unqualified
: TypeOfKind::Qualified);
if (Result.isNull())
return QualType();
}
@ -6234,7 +6240,7 @@ QualType TreeTransform<Derived>::TransformTypeOfType(TypeLocBuilder &TLB,
NewTL.setTypeofLoc(TL.getTypeofLoc());
NewTL.setLParenLoc(TL.getLParenLoc());
NewTL.setRParenLoc(TL.getRParenLoc());
NewTL.setUnderlyingTInfo(New_Under_TI);
NewTL.setUnmodifiedTInfo(New_Under_TI);
return Result;
}
@ -14719,14 +14725,15 @@ QualType TreeTransform<Derived>::RebuildUnresolvedUsingType(SourceLocation Loc,
}
template <typename Derived>
QualType TreeTransform<Derived>::RebuildTypeOfExprType(Expr *E,
SourceLocation) {
return SemaRef.BuildTypeofExprType(E);
QualType TreeTransform<Derived>::RebuildTypeOfExprType(Expr *E, SourceLocation,
TypeOfKind Kind) {
return SemaRef.BuildTypeofExprType(E, Kind);
}
template<typename Derived>
QualType TreeTransform<Derived>::RebuildTypeOfType(QualType Underlying) {
return SemaRef.Context.getTypeOfType(Underlying);
QualType TreeTransform<Derived>::RebuildTypeOfType(QualType Underlying,
TypeOfKind Kind) {
return SemaRef.Context.getTypeOfType(Underlying, Kind);
}
template <typename Derived>

View File

@ -6662,7 +6662,7 @@ void TypeLocReader::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) {
TL.setTypeofLoc(readSourceLocation());
TL.setLParenLoc(readSourceLocation());
TL.setRParenLoc(readSourceLocation());
TL.setUnderlyingTInfo(GetTypeSourceInfo());
TL.setUnmodifiedTInfo(GetTypeSourceInfo());
}
void TypeLocReader::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {

View File

@ -436,7 +436,7 @@ void TypeLocWriter::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) {
addSourceLocation(TL.getTypeofLoc());
addSourceLocation(TL.getLParenLoc());
addSourceLocation(TL.getRParenLoc());
Record.AddTypeSourceInfo(TL.getUnderlyingTInfo());
Record.AddTypeSourceInfo(TL.getUnmodifiedTInfo());
}
void TypeLocWriter::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {

92
clang/test/C/C2x/n2927.c Normal file
View File

@ -0,0 +1,92 @@
// RUN: %clang_cc1 -verify -std=c2x %s
/* WG14 N2927: yes
* Not-so-magic: typeof
*/
// These examples originated in WG14 N2927 but were modified to test particular
// compiler behaviors. Each of these examples come from C2x 6.7.2.5.
// EXAMPLE 1
typeof(1 + 1) func();
int func();
// EXAMPLE 2
const _Atomic int purr = 0;
const int meow = 1;
const char *const mew[] = {
"aardvark",
"bluejay",
"catte",
};
extern typeof_unqual(purr) plain_purr;
extern int plain_purr;
extern typeof(_Atomic typeof(meow)) atomic_meow;
extern const _Atomic int atomic_meow;
extern typeof(mew) mew_array;
extern const char *const mew_array[3];
extern typeof_unqual(mew) mew2_array;
extern const char *mew2_array[3];
// EXAMPLE 3
void foo(int argc, char *argv[]) { // expected-note 2 {{declared here}}
_Static_assert(sizeof(typeof('p')) == sizeof(int));
_Static_assert(sizeof(typeof('p')) == sizeof('p'));
_Static_assert(sizeof(typeof((char)'p')) == sizeof(char));
_Static_assert(sizeof(typeof((char)'p')) == sizeof((char)'p'));
_Static_assert(sizeof(typeof("meow")) == sizeof(char[5]));
_Static_assert(sizeof(typeof("meow")) == sizeof("meow"));
_Static_assert(sizeof(typeof(argc)) == sizeof(int));
_Static_assert(sizeof(typeof(argc)) == sizeof(argc));
_Static_assert(sizeof(typeof(argv)) == sizeof(char**));
_Static_assert(sizeof(typeof(argv)) == sizeof(argv)); // expected-warning {{sizeof on array function parameter will return size of 'char **' instead of 'char *[]'}}
_Static_assert(sizeof(typeof_unqual('p')) == sizeof(int));
_Static_assert(sizeof(typeof_unqual('p')) == sizeof('p'));
_Static_assert(sizeof(typeof_unqual((char)'p')) == sizeof(char));
_Static_assert(sizeof(typeof_unqual((char)'p')) == sizeof((char)'p'));
_Static_assert(sizeof(typeof_unqual("meow")) == sizeof(char[5]));
_Static_assert(sizeof(typeof_unqual("meow")) == sizeof("meow"));
_Static_assert(sizeof(typeof_unqual(argc)) == sizeof(int));
_Static_assert(sizeof(typeof_unqual(argc)) == sizeof(argc));
_Static_assert(sizeof(typeof_unqual(argv)) == sizeof(char**));
_Static_assert(sizeof(typeof_unqual(argv)) == sizeof(argv)); // expected-warning {{sizeof on array function parameter will return size of 'char **' instead of 'char *[]'}}
}
// EXAMPLE 4
void bar(int argc) {
extern int val;
extern typeof(typeof_unqual(typeof(argc)))val;
}
// EXAMPLE 5 is tested by n2927_2.c because it is a codegen test.
// EXAMPLE 6
extern const char *y[4];
extern typeof(typeof(const char*)[4]) y;
// EXAMPLE 7
void f(int);
void g(double);
typeof(f(5)) g(double x); // g has type "void(double)"
extern void (*h)(double);
extern typeof(g)* h; // h has type "void(*)(double)"
extern typeof(true ? g : 0) h; // h has type "void(*)(double)"
void j(double *, double **);
void j(double A[5], typeof(A)* B); // j has type "void(double*, double**)"
extern typeof(double[]) D; // D has an incomplete type
extern double C[2];
extern typeof(D) C; // C has type "double[2]"
typeof(D) D = { 5, 8.9, 0.1, 99 }; // D is now completed to "double[4]"
extern double E[4];
extern typeof(D) E; // E has type "double[4]" from Ds completed type

View File

@ -0,0 +1,21 @@
// RUN: %clang_cc1 -emit-llvm -o - -std=c2x %s | FileCheck %s
// C2x 6.7.2.5 EXAMPLE 5
unsigned long long vla_size(int n) {
// CHECK: vla_size
return sizeof(
typeof_unqual(char[n + 3])
); // execution-time sizeof, translation-time typeof operation
// CHECK: [[N_ADDR:%.*]] = alloca i32
// CHECK: store i32 {{%.*}} ptr [[N_ADDR]]
// CHECK: [[N:%.*]] = load i32, ptr [[N_ADDR]]
// CHECK: [[TEMP:%.*]] = add nsw i32 [[N]], 3
// CHECK: [[RET:%.*]] = zext i32 [[TEMP]] to i64
// CHECK: ret i64 [[RET]]
}
int main() {
return (int)vla_size(10); // vla_size returns 13
}

9
clang/test/C/C2x/n2930.c Normal file
View File

@ -0,0 +1,9 @@
// RUN: %clang_cc1 -verify -std=c2x %s
/* WG14 N2930: yes
* Consider renaming remove_quals
*/
int remove_quals;
int typeof_unqual; // expected-error {{expected '(' after 'typeof_unqual'}}
typeof_unqual(remove_quals) val;

View File

@ -44,6 +44,7 @@ C2x_KEYWORD(true);
C2x_KEYWORD(false);
C2x_KEYWORD(static_assert);
C2x_KEYWORD(typeof);
C2x_KEYWORD(typeof_unqual);
C2x_KEYWORD(thread_local);
C2x_KEYWORD(alignas);
C2x_KEYWORD(alignof);
@ -97,6 +98,7 @@ void has_static_assert();
char false; // c89-warning {{'false' is a keyword in C2x}}
float alignof; // c89-warning {{'alignof' is a keyword in C2x}}
int typeof; // c89-warning {{'typeof' is a keyword in C2x}}
int typeof_unqual; // c89-warning {{'typeof_unqual' is a keyword in C2x}}
int alignas; // c89-warning {{'alignas' is a keyword in C2x}}
int static_assert; // c89-warning {{'static_assert' is a keyword in C2x}}

View File

@ -0,0 +1,29 @@
// RUN: %clang_cc1 -verify=c2x -std=c2x %s
// RUN: %clang_cc1 -verify=c11 -std=c11 %s
// RUN: %clang_cc1 -verify=gnu11 -std=gnu11 %s
// RUN: %clang_cc1 -verify=pedantic -pedantic -std=gnu11 -Wno-comment %s
// RUN: %clang_cc1 -verify=compat -std=c2x -Wpre-c2x-compat %s
// c2x-no-diagnostics
// Exercise the various circumstances under which we will diagnose use of
// typeof and typeof_unqual as either an extension or as a compatability
// warning. Note that GCC exposes 'typeof' as a non-conforming extension in
// standards before C2x, and Clang has followed suit. Neither compiler exposes
// 'typeof_unqual' as a non-conforming extension.
// Show what happens with the underscored version of the keyword, which is a
// conforming extension.
__typeof__(int) i = 12;
// Show what happens with a regular 'typeof' use.
typeof(i) j = 12; // c11-error {{expected function body after function declarator}} \
pedantic-warning {{extension used}} \
compat-warning {{'typeof' is incompatible with C standards before C2x}}
// Same for 'typeof_unqual'.
typeof_unqual(j) k = 12; // c11-error {{expected function body after function declarator}} \
gnu11-error {{expected function body after function declarator}} \
pedantic-error {{expected function body after function declarator}} \
compat-warning {{'typeof_unqual' is incompatible with C standards before C2x}}

View File

@ -0,0 +1,44 @@
// RUN: %clang_cc1 -verify -std=c2x %s
// Demonstrate that we don't support the expression form without parentheses in
// C2x mode.
typeof 0 int i = 12; // expected-error {{expected '(' after 'typeof'}} expected-error {{expected identifier or '('}}
typeof 0 j = 12; // expected-error {{expected '(' after 'typeof'}} expected-error {{expected identifier or '('}}
typeof_unqual 0 k = 12; // expected-error {{expected '(' after 'typeof_unqual'}} expected-error {{expected identifier or '('}}
typeof_unqual 0 int l = 12; // expected-error {{expected '(' after 'typeof_unqual'}} expected-error {{expected identifier or '('}}
// Show that combining typeof with another type specifier fails, but otherwise
// the expression and type forms are both parsed properly.
typeof(0) int a = 12; // expected-error {{cannot combine with previous 'typeof' declaration specifier}}
typeof(0) b = 12;
typeof_unqual(0) int c = 12; // expected-error {{cannot combine with previous 'typeof_unqual' declaration specifier}}
typeof_unqual(0) d = 12;
typeof(int) e = 12;
typeof_unqual(int) f = 12;
// Show that we can parse nested constructs of both forms.
typeof(typeof(0)) w;
typeof_unqual(typeof(0)) x;
typeof(typeof_unqual(0)) y;
typeof_unqual(typeof_unqual(0)) z;
// Show that you can spell the type in functions, structures, or as the base
// type of an enumeration.
typeof(b) func1(typeof(b) c);
typeof_unqual(b) func2(typeof_unqual(b) c);
struct S {
typeof(b) i;
typeof_unqual(b) j;
} s;
enum E1 : typeof(b) { FirstZero };
enum E2 : typeof_unqual(b) { SecondZero };
// Show that you can use this type in place of another type and everything
// works as expected.
_Static_assert(__builtin_offsetof(typeof(struct S), i) == 0);
_Static_assert(__builtin_offsetof(typeof(s), i) == 0);
_Static_assert(__builtin_offsetof(typeof_unqual(struct S), i) == 0);
_Static_assert(__builtin_offsetof(typeof_unqual(s), i) == 0);

View File

@ -0,0 +1,94 @@
// RUN: %clang_cc1 -verify -std=c2x %s
// Demonstrate that we get the correct type information. Do this by leaning
// heavily on redeclarations needing to use the same type for both decls.
extern int i;
extern typeof(i) i;
extern typeof_unqual(i) i;
extern const int j;
extern typeof(j) j;
extern const int n; // expected-note 2 {{previous declaration is here}}
extern typeof(i) n; // expected-error {{redeclaration of 'n' with a different type: 'typeof (i)' (aka 'int') vs 'const int'}}
extern typeof_unqual(n) n; // expected-error {{redeclaration of 'n' with a different type: 'typeof_unqual (n)' (aka 'int') vs 'const int'}}
// Ensure we get a redeclaration error here for the types not matching.
extern typeof(j) k; // expected-note {{previous declaration is here}}
extern typeof_unqual(j) k; // expected-error {{redeclaration of 'k' with a different type: 'typeof_unqual (j)' (aka 'int') vs 'typeof (j)' (aka 'const int')}}
// Make sure the type-form of the operator also works.
extern typeof(int) l;
extern typeof_unqual(const int) l;
extern typeof(const int) m; // expected-note {{previous declaration is here}}
extern typeof_unqual(const int) m; // expected-error {{redeclaration of 'm' with a different type: 'typeof_unqual(const int)' (aka 'int') vs 'typeof(const int)' (aka 'const int')}}
// Show that we can use an incomplete type which is then completed later.
extern typeof(struct T) *o;
struct T { int a; } t;
extern typeof(struct T) *o;
extern typeof(t) *o;
extern typeof(&t) o;
extern typeof_unqual(volatile struct T) *o;
extern typeof_unqual(t) *o;
extern typeof_unqual(&t) o;
// Show that we properly strip the _Atomic qualifier.
extern _Atomic int i2;
extern _Atomic(int) i2;
extern typeof(i2) i2; // expected-note {{previous declaration is here}}
extern typeof_unqual(i2) i2; // expected-error {{redeclaration of 'i2' with a different type: 'typeof_unqual (i2)' (aka 'int') vs 'typeof (i2)' (aka '_Atomic(int)')}}
// We cannot take the type of a bit-field.
struct S {
int bit : 4;
} s;
typeof(s.bit) nope1; // expected-error {{invalid application of 'typeof' to bit-field}}
typeof_unqual(s.bit) nope2; // expected-error {{invalid application of 'typeof_unqual' to bit-field}}
// Show that we properly resolve nested typeof specifiers.
extern typeof(typeof(0)) i3;
extern typeof(typeof(int)) i3;
extern typeof(typeof_unqual(0)) i3;
extern typeof(typeof_unqual(int)) i3;
extern typeof_unqual(typeof(0)) i3;
extern typeof_unqual(typeof(int)) i3;
extern typeof_unqual(typeof_unqual(0)) i3;
extern typeof_unqual(typeof_unqual(int)) i3;
extern typeof(typeof_unqual(j)) i3;
extern typeof(typeof_unqual(const int)) i3;
extern typeof_unqual(typeof(j)) i3;
extern typeof_unqual(typeof(const int)) i3;
extern typeof_unqual(typeof_unqual(j)) i3;
extern typeof_unqual(typeof_unqual(const int)) i3;
// Both of these result in a const int rather than an int.
extern typeof(typeof(j)) i4;
extern typeof(typeof(const int)) i4;
// Ensure that redundant qualifiers are allowed, same as with typedefs.
typedef const int CInt;
extern CInt i4;
extern const CInt i4;
extern const typeof(j) i4;
extern const typeof(const int) i4;
extern const typeof(CInt) i4;
// Qualifiers are not redundant here, but validating that the qualifiers are
// still honored.
extern const typeof_unqual(j) i4;
extern const typeof_unqual(const int) i4;
extern const typeof_unqual(CInt) i4;
// Show that type attributes are stripped from the unqualified version.
extern __attribute__((address_space(0))) int type_attr_test_2_obj;
extern int type_attr_test_2;
extern typeof_unqual(type_attr_test_2_obj) type_attr_test_2; // expected-note {{previous declaration is here}}
extern __attribute__((address_space(0))) int type_attr_test_2; // expected-error {{redeclaration of 'type_attr_test_2' with a different type: '__attribute__((address_space(0))) int' vs 'typeof_unqual (type_attr_test_2_obj)' (aka 'int')}}
// Ensure that an invalid type doesn't cause crashes.
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;

View File

@ -1817,7 +1817,7 @@ bool CursorVisitor::VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) {
}
bool CursorVisitor::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) {
if (TypeSourceInfo *TSInfo = TL.getUnderlyingTInfo())
if (TypeSourceInfo *TSInfo = TL.getUnmodifiedTInfo())
return Visit(TSInfo->getTypeLoc());
return false;

View File

@ -1083,17 +1083,11 @@ conformance.</p>
</tr>
<tr> <!-- Feb 2022 -->
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2927.htm">N2927</a></td>
<td class="partial" align="center">
<details><summary>Partial</summary>
Clang supports <code>typeof</code> in GNU standards mode, but its
compatibility with this proposal is unknown. Also, Clang does not yet
support remove_quals.
</details>
</td>
<td class="unreleased" align="center">Clang 16</td>
</tr>
<tr> <!-- Jul 2022 -->
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2930.pdf">N2930</a></td>
<td class="none" align="center">No</td>
<td class="unreleased" align="center">Clang 16</td>
</tr>
<tr>
<td>Type annex tgmath narrowing macros with integer args v2</td>