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 @@ -4383,7 +4383,11 @@ class UnaryTransformType : public Type { public: enum UTTKind { - EnumUnderlyingType + EnumUnderlyingType, + RemoveReferenceType, + RemoveCV, + RemoveConst, + RemoveVolatile, }; private: 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 @@ -83,16 +83,20 @@ TST_struct, TST_class, // C++ class type TST_interface, // C++ (Microsoft-specific) __interface type + TST_decltype, // C++11 decltype + TST_typeofExpr, TST_typename, // Typedef, C++ class-name or enum name, etc. TST_typeofType, - TST_typeofExpr, - TST_decltype, // C++11 decltype TST_underlyingType, // __underlying_type for C++11 + TST_removeReferenceType, // __remove_reference + TST_removeCV, // __remove_cv + TST_removeConst, // __remove_const + TST_removeVolatile, // __remove_volatile + TST_atomic, // C11 _Atomic TST_auto, // C++11 auto TST_decltype_auto, // C++1y decltype(auto) TST_auto_type, // __auto_type extension TST_unknown_anytype, // __unknown_anytype extension - TST_atomic, // C11 _Atomic #define GENERIC_IMAGE_TYPE(ImgType, Id) TST_##ImgType##_t, // OpenCL image types #include "clang/Basic/OpenCLImageTypes.def" TST_error // erroneous type 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 @@ -487,6 +487,10 @@ TYPE_TRAIT_1(__has_unique_object_representations, HasUniqueObjectRepresentations, KEYCXX) KEYWORD(__underlying_type , KEYCXX) +KEYWORD(__remove_reference , KEYCXX) +KEYWORD(__remove_cv , KEYCXX) +KEYWORD(__remove_const , KEYCXX) +KEYWORD(__remove_volatile , KEYCXX) // Clang-only C++ Type Traits TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2715,6 +2715,8 @@ SourceLocation StartLoc, SourceLocation EndLoc); void ParseUnderlyingTypeSpecifier(DeclSpec &DS); + DeclSpec::TST TypeTransformTokToDeclSpec(); + void ParseTypeTransformTypeSpecifier(DeclSpec &DS); void ParseAtomicSpecifier(DeclSpec &DS); ExprResult ParseAlignArgument(SourceLocation Start, 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 @@ -294,16 +294,20 @@ static const TST TST_struct = clang::TST_struct; static const TST TST_interface = clang::TST_interface; static const TST TST_class = clang::TST_class; - 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_decltype = clang::TST_decltype; static const TST TST_decltype_auto = clang::TST_decltype_auto; + static const TST TST_typeofExpr = clang::TST_typeofExpr; + static const TST TST_typename = clang::TST_typename; + static const TST TST_typeofType = clang::TST_typeofType; static const TST TST_underlyingType = clang::TST_underlyingType; + static const TST TST_removeReferenceType = clang::TST_removeReferenceType; + static const TST TST_removeCV = clang::TST_removeCV; + static const TST TST_removeConst = clang::TST_removeConst; + static const TST TST_removeVolatile = clang::TST_removeVolatile; + static const TST TST_atomic = clang::TST_atomic; static const TST TST_auto = clang::TST_auto; static const TST TST_auto_type = clang::TST_auto_type; static const TST TST_unknown_anytype = clang::TST_unknown_anytype; - static const TST TST_atomic = clang::TST_atomic; #define GENERIC_IMAGE_TYPE(ImgType, Id) \ static const TST TST_##ImgType##_t = clang::TST_##ImgType##_t; #include "clang/Basic/OpenCLImageTypes.def" @@ -409,8 +413,7 @@ ObjCDeclSpec *ObjCQualifiers; static bool isTypeRep(TST T) { - return (T == TST_typename || T == TST_typeofType || - T == TST_underlyingType || T == TST_atomic); + return (TST_typename <= T && T <= TST_atomic); } static bool isExprRep(TST T) { return (T == TST_typeofExpr || T == TST_decltype); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -3497,6 +3497,20 @@ case UnaryTransformType::EnumUnderlyingType: Out << "3eut"; break; + case UnaryTransformType::RemoveReferenceType: + Out << "3rrt"; + break; + case UnaryTransformType::RemoveCV: + Out << "3rcvt"; + break; + break; + case UnaryTransformType::RemoveConst: + Out << "3rct"; + break; + break; + case UnaryTransformType::RemoveVolatile: + Out << "3rvt"; + break; } } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -621,6 +621,18 @@ case UnaryTransformType::EnumUnderlyingType: JOS.attribute("transformKind", "underlying_type"); break; + case UnaryTransformType::RemoveReferenceType: + JOS.attribute("transformKind", "remove_reference"); + break; + case UnaryTransformType::RemoveCV: + JOS.attribute("transformKind", "remove_cv"); + break; + case UnaryTransformType::RemoveConst: + JOS.attribute("transformKind", "remove_const"); + break; + case UnaryTransformType::RemoveVolatile: + JOS.attribute("transformKind", "remove_volatile"); + break; } } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1220,6 +1220,18 @@ case UnaryTransformType::EnumUnderlyingType: OS << " underlying_type"; break; + case UnaryTransformType::RemoveReferenceType: + OS << " remove_reference"; + break; + case UnaryTransformType::RemoveCV: + OS << " remove_cv"; + break; + case UnaryTransformType::RemoveConst: + OS << " remove_const"; + break; + case UnaryTransformType::RemoveVolatile: + OS << " remove_volatile"; + break; } } 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 @@ -21,6 +21,7 @@ #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" +#include "clang/AST/TextNodeDumper.h" #include "clang/AST/Type.h" #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/ExceptionSpecificationType.h" @@ -1019,16 +1020,14 @@ raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - switch (T->getUTTKind()) { - case UnaryTransformType::EnumUnderlyingType: - OS << "__underlying_type("; - print(T->getBaseType(), OS, StringRef()); - OS << ')'; - spaceBeforePlaceHolder(OS); - return; - } - - printBefore(T->getBaseType(), OS); + TextNodeDumper textDumper(OS, /*ShowColors*/false, + &T->getAsRecordDecl()->getASTContext().getSourceManager(), + Policy, /*Traits*/nullptr); + textDumper.VisitUnaryTransformType(T); + OS << '('; + print(T->getBaseType(), OS, StringRef()); + OS << ')'; + spaceBeforePlaceHolder(OS); } void TypePrinter::printUnaryTransformAfter(const UnaryTransformType *T, @@ -1037,6 +1036,10 @@ switch (T->getUTTKind()) { case UnaryTransformType::EnumUnderlyingType: + case UnaryTransformType::RemoveReferenceType: + case UnaryTransformType::RemoveCV: + case UnaryTransformType::RemoveConst: + case UnaryTransformType::RemoveVolatile: return; } diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp --- a/clang/lib/Format/FormatToken.cpp +++ b/clang/lib/Format/FormatToken.cpp @@ -55,6 +55,10 @@ case tok::kw_wchar_t: case tok::kw_bool: case tok::kw___underlying_type: + case tok::kw___remove_reference: + case tok::kw___remove_cv: + case tok::kw___remove_const: + case tok::kw___remove_volatile: case tok::annot_typename: case tok::kw_char8_t: case tok::kw_char16_t: diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1662,6 +1662,10 @@ .Case("__array_extent", true) .Case("__reference_binds_to_temporary", true) .Case("__underlying_type", true) + .Case("__remove_reference", true) + .Case("__remove_cv", true) + .Case("__remove_const", true) + .Case("__remove_volatile", true) .Default(false); } else { return llvm::StringSwitch(II->getName()) 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 @@ -4012,6 +4012,13 @@ case tok::kw___underlying_type: ParseUnderlyingTypeSpecifier(DS); continue; + + case tok::kw___remove_reference: + case tok::kw___remove_cv: + case tok::kw___remove_const: + case tok::kw___remove_volatile: + ParseTypeTransformTypeSpecifier(DS); + continue; case tok::kw__Atomic: // C11 6.7.2.4/4: diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1092,6 +1092,52 @@ DS.setTypeofParensRange(T.getRange()); } +DeclSpec::TST Parser::TypeTransformTokToDeclSpec() { + switch (Tok.getKind()) { + case tok::kw___remove_reference: + return DeclSpec::TST_removeReferenceType; + case tok::kw___remove_cv: + return DeclSpec::TST_removeCV; + case tok::kw___remove_const: + return DeclSpec::TST_removeConst; + case tok::kw___remove_volatile: + return DeclSpec::TST_removeVolatile; + default: + assert(false && "Not a reference type specifier"); + } +} + +void Parser::ParseTypeTransformTypeSpecifier(DeclSpec &DS) { + // FIXME: static_cast(Tok.getKind() - base); + DeclSpec::TST TypeTransformTST = TypeTransformTokToDeclSpec(); + + SourceLocation StartLoc = ConsumeToken(); + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, + "type transformation builtin", + tok::r_paren)) return; + + TypeResult BaseTyResult = ParseTypeName(); + if (BaseTyResult.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + + T.consumeClose(); + if (T.getCloseLocation().isInvalid()) return; + + const char *PrevSpec; + unsigned DiagID; + if (DS.SetTypeSpecType(TypeTransformTST, + StartLoc, PrevSpec, + DiagID, BaseTyResult.get(), + Actions.getASTContext().getPrintingPolicy())) { + Diag(StartLoc, DiagID) << PrevSpec; + } + DS.setTypeofParensRange(T.getRange()); +} + + /// ParseBaseTypeSpecifier - Parse a C++ base-type-specifier which is either a /// class name or decltype-specifier. Note that we only check that the result /// names a type; semantic analysis will need to verify that the type names a 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 @@ -383,6 +383,10 @@ return false; case TST_underlyingType: + case TST_removeReferenceType: + case TST_removeCV: + case TST_removeConst: + case TST_removeVolatile: case TST_typename: case TST_typeofType: { QualType QT = DS.getRepAsType().get(); @@ -562,6 +566,10 @@ case DeclSpec::TST_decltype: return "(decltype)"; case DeclSpec::TST_decltype_auto: return "decltype(auto)"; case DeclSpec::TST_underlyingType: return "__underlying_type"; + case DeclSpec::TST_removeReferenceType: return "__remove_reference"; + case DeclSpec::TST_removeCV: return "__remove_cv"; + case DeclSpec::TST_removeConst: return "__remove_const"; + case DeclSpec::TST_removeVolatile: return "__remove_volatile"; case DeclSpec::TST_unknown_anytype: return "__unknown_anytype"; case DeclSpec::TST_atomic: return "_Atomic"; #define GENERIC_IMAGE_TYPE(ImgType, Id) \ 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 @@ -143,6 +143,10 @@ case tok::kw_wchar_t: case tok::kw_bool: case tok::kw___underlying_type: + case tok::kw___remove_reference: + case tok::kw___remove_cv: + case tok::kw___remove_const: + case tok::kw___remove_volatile: case tok::kw___auto_type: return true; @@ -5470,6 +5474,10 @@ case DeclSpec::TST_typename: case DeclSpec::TST_typeofType: case DeclSpec::TST_underlyingType: + case DeclSpec::TST_removeReferenceType: + case DeclSpec::TST_removeCV: + case DeclSpec::TST_removeConst: + case DeclSpec::TST_removeVolatile: case DeclSpec::TST_atomic: { // Grab the type from the parser. TypeSourceInfo *TSI = nullptr; 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 @@ -838,6 +838,10 @@ case TST_typename: case TST_typeofType: case TST_underlyingType: + case TST_removeReferenceType: + case TST_removeCV: + case TST_removeConst: + case TST_removeVolatile: case TST_atomic: { QualType T = DS.getRepAsType().get(); if (!T.isNull() && T->containsUnexpandedParameterPack()) 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 @@ -1253,6 +1253,24 @@ return OpenCLAccessAttr::Keyword_read_only; } +static UnaryTransformType::UTTKind +TSTToUnaryTransformType(DeclSpec::TST SwitchTST) { + switch (SwitchTST) { + case TST_removeCV: + return UnaryTransformType::RemoveCV; + case TST_removeConst: + return UnaryTransformType::RemoveConst; + case TST_removeVolatile: + return UnaryTransformType::RemoveVolatile; + case TST_removeReferenceType: + return UnaryTransformType::RemoveReferenceType; + case TST_underlyingType: + return UnaryTransformType::EnumUnderlyingType; + default: + llvm_unreachable("Cannot map TST to unary transform type"); + } +} + static QualType ConvertConstrainedAutoDeclSpecToType(Sema &S, DeclSpec &DS, AutoTypeKeyword AutoKW) { assert(DS.isConstrainedAuto()); @@ -1605,16 +1623,23 @@ break; } case DeclSpec::TST_underlyingType: + case DeclSpec::TST_removeReferenceType: + case DeclSpec::TST_removeCV: + case DeclSpec::TST_removeConst: + case DeclSpec::TST_removeVolatile: { Result = S.GetTypeFromParser(DS.getRepAsType()); - assert(!Result.isNull() && "Didn't get a type for __underlying_type?"); + assert(!Result.isNull() && + "Type transform builtin may not have received a type."); Result = S.BuildUnaryTransformType(Result, - UnaryTransformType::EnumUnderlyingType, + TSTToUnaryTransformType(DS.getTypeSpecType()), DS.getTypeSpecTypeLoc()); if (Result.isNull()) { - Result = Context.IntTy; + if (DS.getTypeSpecType() == DeclSpec::TST_underlyingType) + Result = Context.IntTy; declarator.setInvalidType(true); } break; + } case DeclSpec::TST_auto: if (DS.isConstrainedAuto()) { @@ -5660,8 +5685,9 @@ TL.setUnderlyingTInfo(TInfo); } void VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { - // FIXME: This holds only because we only have one unary transform. - assert(DS.getTypeSpecType() == DeclSpec::TST_underlyingType); + // Make sure it is a unary transform type + assert(DS.getTypeSpecType() >= DeclSpec::TST_underlyingType && + DS.getTypeSpecType() <= DeclSpec::TST_removeVolatile); TL.setKWLoc(DS.getTypeSpecTypeLoc()); TL.setParensRange(DS.getTypeofParensRange()); assert(DS.getRepAsType()); @@ -8604,6 +8630,36 @@ return Context.getUnaryTransformType(BaseType, Underlying, UnaryTransformType::EnumUnderlyingType); } + case UnaryTransformType::RemoveReferenceType: { + QualType Underlying = BaseType.getNonReferenceType(); + return Context.getUnaryTransformType(BaseType, Underlying, + UnaryTransformType::RemoveReferenceType); + } + case UnaryTransformType::RemoveCV: { + SplitQualType Split = BaseType.getSplitUnqualifiedType(); + Qualifiers Quals = BaseType.getQualifiers(); + Quals.removeConst(); + Quals.removeVolatile(); + QualType Underlying(Split.Ty, Quals.getAsOpaqueValue()); + return Context.getUnaryTransformType(BaseType, Underlying, + UnaryTransformType::RemoveCV); + } + case UnaryTransformType::RemoveConst: { + SplitQualType Split = BaseType.getSplitUnqualifiedType(); + Qualifiers Quals = BaseType.getQualifiers(); + Quals.removeConst(); + QualType Underlying(Split.Ty, Quals.getAsOpaqueValue()); + return Context.getUnaryTransformType(BaseType, Underlying, + UnaryTransformType::RemoveConst); + } + case UnaryTransformType::RemoveVolatile: { + SplitQualType Split = BaseType.getSplitUnqualifiedType(); + Qualifiers Quals = BaseType.getQualifiers(); + Quals.removeVolatile(); + QualType Underlying(Split.Ty, Quals.getAsOpaqueValue()); + return Context.getUnaryTransformType(BaseType, Underlying, + UnaryTransformType::RemoveVolatile); + } } llvm_unreachable("unknown unary transform type"); } diff --git a/clang/test/SemaCXX/remove_cv.cpp b/clang/test/SemaCXX/remove_cv.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/remove_cv.cpp @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -std=c++11 %s + +template +struct remove_const +{ + typedef __remove_const(T) type; +}; + +template +struct remove_volatile +{ + typedef __remove_volatile(T) type; +}; + +template +struct remove_cv +{ + typedef __remove_cv(T) type; +}; + +template +struct test +{ + static const bool value = + __is_same(typename remove_const::type, T) && + __is_same(typename remove_const::type, volatile T) && + __is_same(typename remove_const::type, T) && + + __is_same(typename remove_volatile::type, T) && + __is_same(typename remove_volatile::type, const T) && + __is_same(typename remove_volatile::type, T) && + + __is_same(typename remove_cv::type, T) && + __is_same(typename remove_cv::type, T) && + __is_same(typename remove_cv::type, T) && + __is_same(typename remove_cv::type, T); +}; + +struct Foo { }; + +template +struct Bar { }; + +template +class Baz { }; + +class Biz; + +void x() { } + +static_assert(test::value, ""); +static_assert(test::value, ""); +static_assert(test::value, ""); +static_assert(test::value, ""); +static_assert(test>::value, ""); +static_assert(test>::value, ""); +static_assert(test::value, ""); +static_assert(test::value, ""); diff --git a/clang/test/SemaCXX/remove_reference.cpp b/clang/test/SemaCXX/remove_reference.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/remove_reference.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -std=c++11 %s + +template +struct test_remove_ref_trait +{ + typedef __remove_reference(T) type; +}; + +template +struct test_remove_ref +{ + static const bool value = __is_same(typename test_remove_ref_trait::type, T) && + __is_same(typename test_remove_ref_trait::type, T) && + __is_same(typename test_remove_ref_trait::type, T); +}; + +struct Foo { }; + +template +struct Bar { }; + +template +class Baz { }; + +class Biz; + +void x() { } + +static_assert(test_remove_ref::value, ""); +static_assert(test_remove_ref::value, ""); +static_assert(test_remove_ref::value, ""); +static_assert(test_remove_ref::value, ""); +static_assert(test_remove_ref>::value, ""); +static_assert(test_remove_ref>::value, ""); +static_assert(test_remove_ref::value, ""); +static_assert(test_remove_ref::value, ""); +static_assert(test_remove_ref::value, ""); +static_assert(test_remove_ref::value, ""); +static_assert(test_remove_ref::value, ""); +