diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -311,6 +311,22 @@ ``-Wunused-label`` warning. - Implemented `WG14 N2508 `_, so labels can placed everywhere inside a compound statement. +- Implemented `WG14 N2927 `_, + the Not-so-magic ``typeof`` operator. Also implemented + `WG14 N2930 `_, + 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 ----------------------------- diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1714,9 +1714,9 @@ /// 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; diff --git a/clang/include/clang/AST/CanonicalType.h b/clang/include/clang/AST/CanonicalType.h --- a/clang/include/clang/AST/CanonicalType.h +++ b/clang/include/clang/AST/CanonicalType.h @@ -529,7 +529,7 @@ template<> struct CanProxyAdaptor : public CanProxyBase { - LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getUnderlyingType) + LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getUnmodifiedType) }; template<> diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -138,6 +138,7 @@ 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">; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1070,7 +1070,7 @@ 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 @@ { 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 diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -714,6 +714,12 @@ 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 @@ 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 @@ ConstantArrayTypeBitfields ConstantArrayTypeBits; AttributedTypeBitfields AttributedTypeBits; AutoTypeBitfields AutoTypeBits; + TypeOfBitfields TypeOfBits; BuiltinTypeBitfields BuiltinTypeBits; FunctionTypeBitfields FunctionTypeBits; ObjCObjectTypeBitfields ObjCObjectTypeBits; @@ -4532,18 +4547,22 @@ } }; -/// 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 @@ 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(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; } }; diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -1934,7 +1934,7 @@ }; struct TypeOfTypeLocInfo : public TypeofLocInfo { - TypeSourceInfo* UnderlyingTInfo; + TypeSourceInfo *UnmodifiedTInfo; }; template @@ -2002,16 +2002,16 @@ class TypeOfTypeLoc : public TypeofLikeTypeLoc { 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); diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -397,18 +397,28 @@ 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); }]>; } diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -371,6 +371,9 @@ def ext_auto_type : Extension< "'__auto_type' is a GNU extension">, InGroup; +def warn_c2x_compat_typeof_type_specifier : Warning< + "'%select{typeof|typeof_unqual}0' is incompatible with C standards before " + "C2x">, InGroup, DefaultIgnore; def ext_for_range : ExtWarn< "range-based for loop is a C++11 extension">, InGroup; def warn_cxx98_compat_for_range : Warning< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6530,7 +6530,8 @@ 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< diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -79,8 +79,10 @@ 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" diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -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 @@ // 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(__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 @@ #undef PUNCTUATOR #undef TOK #undef C99_KEYWORD +#undef C2X_KEYWORD diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -289,6 +289,8 @@ 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 @@ 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); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2523,7 +2523,7 @@ // 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); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5581,30 +5581,31 @@ /// 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 @@ /// 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 @@ 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(X)->isUnqual() && + cast(Y)->isUnqual() + ? TypeOfKind::Unqualified + : TypeOfKind::Qualified); case Type::TypeOfExpr: return QualType(); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1370,16 +1370,17 @@ 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) { diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -974,8 +974,8 @@ case Type::TypeOf: if (!IsStructurallyEquivalent(Context, - cast(T1)->getUnderlyingType(), - cast(T2)->getUnderlyingType())) + cast(T1)->getUnmodifiedType(), + cast(T2)->getUnmodifiedType())) return false; break; diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -1061,7 +1061,7 @@ VisitType(T); } void VisitTypeOfType(const TypeOfType *T) { - AddQualType(T->getUnderlyingType()); + AddQualType(T->getUnmodifiedType()); VisitType(T); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3469,27 +3469,37 @@ 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) diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -521,8 +521,8 @@ SourceLocation Loc) { TypeofLikeTypeLoc ::initializeLocal(Context, Loc); - this->getLocalData()->UnderlyingTInfo = Context.getTrivialTypeSourceInfo( - getUnderlyingType(), Loc); + this->getLocalData()->UnmodifiedTInfo = + Context.getTrivialTypeSourceInfo(getUnmodifiedType(), Loc); } void UnaryTransformTypeLoc::initializeLocal(ASTContext &Context, diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1110,7 +1110,7 @@ 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 @@ 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); } diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3287,7 +3287,7 @@ T = cast(T)->getUnderlyingExpr()->getType(); break; case Type::TypeOf: - T = cast(T)->getUnderlyingType(); + T = cast(T)->getUnmodifiedType(); break; case Type::Decltype: T = cast(T)->getUnderlyingType(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4241,8 +4241,9 @@ 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 @@ // 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 @@ 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 @@ /// 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 @@ 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 @@ 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 @@ 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; diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2288,6 +2288,13 @@ /// 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 @@ 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 @@ } 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 @@ 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 diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -384,6 +384,7 @@ 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 @@ #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 @@ 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)"; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5939,6 +5939,7 @@ 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 @@ } case DeclSpec::TST_decltype: + case DeclSpec::TST_typeof_unqualExpr: case DeclSpec::TST_typeofExpr: { Expr *E = DS.getRepAsExpr(); ExprResult Result = S.RebuildExprInCurrentInstantiation(E); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6257,7 +6257,7 @@ } bool UnnamedLocalNoLinkageFinder::VisitTypeOfType(const TypeOfType* T) { - return Visit(T->getUnderlyingType()); + return Visit(T->getUnmodifiedType()); } bool UnnamedLocalNoLinkageFinder::VisitDecltypeType(const DecltypeType*) { diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5987,8 +5987,7 @@ case Type::TypeOf: if (!OnlyDeduced) - MarkUsedTemplateParameters(Ctx, - cast(T)->getUnderlyingType(), + MarkUsedTemplateParameters(Ctx, cast(T)->getUnmodifiedType(), OnlyDeduced, Depth, Used); break; diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -863,6 +863,7 @@ 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 @@ break; } + case TST_typeof_unqualExpr: case TST_typeofExpr: case TST_decltype: case TST_bitint: diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1610,6 +1610,7 @@ // 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 @@ if (const TagType *TT = Result->getAs()) 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 @@ } 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 @@ 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()) DiagnoseUseOfDecl(TT->getDecl(), E->getExprLoc()); } - return Context.getTypeOfExprType(E); + return Context.getTypeOfExprType(E, Kind); } /// getDecltypeForExpr - Given an expr, will return the decltype for diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -963,12 +963,13 @@ /// /// 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 @@ return QualType(); QualType Result = TL.getType(); + bool IsUnqual = Result->getAs()->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(Result); NewTL.setTypeofLoc(TL.getTypeofLoc()); @@ -6218,14 +6221,17 @@ template QualType TreeTransform::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()->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 @@ 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 @@ } template -QualType TreeTransform::RebuildTypeOfExprType(Expr *E, - SourceLocation) { - return SemaRef.BuildTypeofExprType(E); +QualType TreeTransform::RebuildTypeOfExprType(Expr *E, SourceLocation, + TypeOfKind Kind) { + return SemaRef.BuildTypeofExprType(E, Kind); } template -QualType TreeTransform::RebuildTypeOfType(QualType Underlying) { - return SemaRef.Context.getTypeOfType(Underlying); +QualType TreeTransform::RebuildTypeOfType(QualType Underlying, + TypeOfKind Kind) { + return SemaRef.Context.getTypeOfType(Underlying, Kind); } template diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6662,7 +6662,7 @@ TL.setTypeofLoc(readSourceLocation()); TL.setLParenLoc(readSourceLocation()); TL.setRParenLoc(readSourceLocation()); - TL.setUnderlyingTInfo(GetTypeSourceInfo()); + TL.setUnmodifiedTInfo(GetTypeSourceInfo()); } void TypeLocReader::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -436,7 +436,7 @@ addSourceLocation(TL.getTypeofLoc()); addSourceLocation(TL.getLParenLoc()); addSourceLocation(TL.getRParenLoc()); - Record.AddTypeSourceInfo(TL.getUnderlyingTInfo()); + Record.AddTypeSourceInfo(TL.getUnmodifiedTInfo()); } void TypeLocWriter::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { diff --git a/clang/test/C/C2x/n2927.c b/clang/test/C/C2x/n2927.c new file mode 100644 --- /dev/null +++ b/clang/test/C/C2x/n2927.c @@ -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 D痴 completed type diff --git a/clang/test/C/C2x/n2927_2.c b/clang/test/C/C2x/n2927_2.c new file mode 100644 --- /dev/null +++ b/clang/test/C/C2x/n2927_2.c @@ -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 +} + diff --git a/clang/test/C/C2x/n2930.c b/clang/test/C/C2x/n2930.c new file mode 100644 --- /dev/null +++ b/clang/test/C/C2x/n2930.c @@ -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; diff --git a/clang/test/Lexer/keywords_test.c b/clang/test/Lexer/keywords_test.c --- a/clang/test/Lexer/keywords_test.c +++ b/clang/test/Lexer/keywords_test.c @@ -44,6 +44,7 @@ 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 @@ 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}} diff --git a/clang/test/Parser/c2x-typeof-ext-warns.c b/clang/test/Parser/c2x-typeof-ext-warns.c new file mode 100644 --- /dev/null +++ b/clang/test/Parser/c2x-typeof-ext-warns.c @@ -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}} + diff --git a/clang/test/Parser/c2x-typeof.c b/clang/test/Parser/c2x-typeof.c new file mode 100644 --- /dev/null +++ b/clang/test/Parser/c2x-typeof.c @@ -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); + diff --git a/clang/test/Sema/c2x-typeof.c b/clang/test/Sema/c2x-typeof.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/c2x-typeof.c @@ -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; diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1817,7 +1817,7 @@ } bool CursorVisitor::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) { - if (TypeSourceInfo *TSInfo = TL.getUnderlyingTInfo()) + if (TypeSourceInfo *TSInfo = TL.getUnmodifiedTInfo()) return Visit(TSInfo->getTypeLoc()); return false; diff --git a/clang/www/c_status.html b/clang/www/c_status.html --- a/clang/www/c_status.html +++ b/clang/www/c_status.html @@ -1083,17 +1083,11 @@ N2927 - -
Partial - Clang supports typeof in GNU standards mode, but its - compatibility with this proposal is unknown. Also, Clang does not yet - support remove_quals. -
- + Clang 16 N2930 - No + Clang 16 Type annex tgmath narrowing macros with integer args v2