From: Sebastian Redl Date: Mon, 13 Sep 2010 20:56:31 +0000 (+0000) Subject: Eagerly evaluate type traits in Sema instead of lazily in AST. They actually need... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0dfd848fa4c9664852ba8c929a8bd3fce93ddca2;p=clang Eagerly evaluate type traits in Sema instead of lazily in AST. They actually need Sema access to be correct, fixes coming up. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@113782 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 2ad7285f44..1424d8899b 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1371,8 +1371,10 @@ public: /// __is_pod(int) == true /// __is_enum(std::string) == false class UnaryTypeTraitExpr : public Expr { - /// UTT - The trait. - UnaryTypeTrait UTT; + /// UTT - The trait. A UnaryTypeTrait enum in MSVC compat unsigned. + unsigned UTT : 31; + /// The value of the type trait. Unspecified if dependent. + bool Value : 1; /// Loc - The location of the type trait keyword. SourceLocation Loc; @@ -1380,29 +1382,30 @@ class UnaryTypeTraitExpr : public Expr { /// RParen - The location of the closing paren. SourceLocation RParen; + /// The type being queried. TypeSourceInfo *QueriedType; public: UnaryTypeTraitExpr(SourceLocation loc, UnaryTypeTrait utt, - TypeSourceInfo *queried, + TypeSourceInfo *queried, bool value, SourceLocation rparen, QualType ty) : Expr(UnaryTypeTraitExprClass, ty, false, queried->getType()->isDependentType()), - UTT(utt), Loc(loc), RParen(rparen), QueriedType(queried) { } + UTT(utt), Value(value), Loc(loc), RParen(rparen), QueriedType(queried) { } explicit UnaryTypeTraitExpr(EmptyShell Empty) - : Expr(UnaryTypeTraitExprClass, Empty), UTT((UnaryTypeTrait)0), + : Expr(UnaryTypeTraitExprClass, Empty), UTT(0), Value(false), QueriedType() { } virtual SourceRange getSourceRange() const { return SourceRange(Loc, RParen);} - UnaryTypeTrait getTrait() const { return UTT; } + UnaryTypeTrait getTrait() const { return static_cast(UTT); } QualType getQueriedType() const { return QueriedType->getType(); } TypeSourceInfo *getQueriedTypeSourceInfo() const { return QueriedType; } - bool EvaluateTrait(ASTContext&) const; + bool getValue() const { return Value; } static bool classof(const Stmt *T) { return T->getStmtClass() == UnaryTypeTraitExprClass; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index e0397075d7..081ae60040 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1192,10 +1192,9 @@ public: /// Member name lookup, which finds the names of /// class/struct/union members. LookupMemberName, - // Look up of an operator name (e.g., operator+) for use with - // operator overloading. This lookup is similar to ordinary name - // lookup, but will ignore any declarations that are class - // members. + /// Look up of an operator name (e.g., operator+) for use with + /// operator overloading. This lookup is similar to ordinary name + /// lookup, but will ignore any declarations that are class members. LookupOperatorName, /// Look up of a name that precedes the '::' scope resolution /// operator in C++. This lookup completely ignores operator, object, diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index fd22db277b..a6aeef1ba8 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -327,204 +327,6 @@ StmtIterator DependentScopeDeclRefExpr::child_end() { return child_iterator(); } -bool UnaryTypeTraitExpr::EvaluateTrait(ASTContext& C) const { - QualType T = getQueriedType(); - switch(UTT) { - default: assert(false && "Unknown type trait or not implemented"); - case UTT_IsPOD: return T->isPODType(); - case UTT_IsLiteral: return T->isLiteralType(); - case UTT_IsClass: // Fallthrough - case UTT_IsUnion: - if (const RecordType *Record = T->getAs()) { - bool Union = Record->getDecl()->isUnion(); - return UTT == UTT_IsUnion ? Union : !Union; - } - return false; - case UTT_IsEnum: return T->isEnumeralType(); - case UTT_IsPolymorphic: - if (const RecordType *Record = T->getAs()) { - // Type traits are only parsed in C++, so we've got CXXRecords. - return cast(Record->getDecl())->isPolymorphic(); - } - return false; - case UTT_IsAbstract: - if (const RecordType *RT = T->getAs()) - return cast(RT->getDecl())->isAbstract(); - return false; - case UTT_IsEmpty: - if (const RecordType *Record = T->getAs()) { - return !Record->getDecl()->isUnion() - && cast(Record->getDecl())->isEmpty(); - } - return false; - case UTT_HasTrivialConstructor: - // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: - // If __is_pod (type) is true then the trait is true, else if type is - // a cv class or union type (or array thereof) with a trivial default - // constructor ([class.ctor]) then the trait is true, else it is false. - if (T->isPODType()) - return true; - if (const RecordType *RT = - C.getBaseElementType(T)->getAs()) - return cast(RT->getDecl())->hasTrivialConstructor(); - return false; - case UTT_HasTrivialCopy: - // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: - // If __is_pod (type) is true or type is a reference type then - // the trait is true, else if type is a cv class or union type - // with a trivial copy constructor ([class.copy]) then the trait - // is true, else it is false. - if (T->isPODType() || T->isReferenceType()) - return true; - if (const RecordType *RT = T->getAs()) - return cast(RT->getDecl())->hasTrivialCopyConstructor(); - return false; - case UTT_HasTrivialAssign: - // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: - // If type is const qualified or is a reference type then the - // trait is false. Otherwise if __is_pod (type) is true then the - // trait is true, else if type is a cv class or union type with - // a trivial copy assignment ([class.copy]) then the trait is - // true, else it is false. - // Note: the const and reference restrictions are interesting, - // given that const and reference members don't prevent a class - // from having a trivial copy assignment operator (but do cause - // errors if the copy assignment operator is actually used, q.v. - // [class.copy]p12). - - if (C.getBaseElementType(T).isConstQualified()) - return false; - if (T->isPODType()) - return true; - if (const RecordType *RT = T->getAs()) - return cast(RT->getDecl())->hasTrivialCopyAssignment(); - return false; - case UTT_HasTrivialDestructor: - // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: - // If __is_pod (type) is true or type is a reference type - // then the trait is true, else if type is a cv class or union - // type (or array thereof) with a trivial destructor - // ([class.dtor]) then the trait is true, else it is - // false. - if (T->isPODType() || T->isReferenceType()) - return true; - if (const RecordType *RT = - C.getBaseElementType(T)->getAs()) - return cast(RT->getDecl())->hasTrivialDestructor(); - return false; - // TODO: Propagate nothrowness for implicitly declared special members. - case UTT_HasNothrowAssign: - // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: - // If type is const qualified or is a reference type then the - // trait is false. Otherwise if __has_trivial_assign (type) - // is true then the trait is true, else if type is a cv class - // or union type with copy assignment operators that are known - // not to throw an exception then the trait is true, else it is - // false. - if (C.getBaseElementType(T).isConstQualified()) - return false; - if (T->isReferenceType()) - return false; - if (T->isPODType()) - return true; - if (const RecordType *RT = T->getAs()) { - CXXRecordDecl* RD = cast(RT->getDecl()); - if (RD->hasTrivialCopyAssignment()) - return true; - - bool FoundAssign = false; - bool AllNoThrow = true; - DeclarationName Name = C.DeclarationNames.getCXXOperatorName(OO_Equal); - DeclContext::lookup_const_iterator Op, OpEnd; - for (llvm::tie(Op, OpEnd) = RD->lookup(Name); - Op != OpEnd; ++Op) { - CXXMethodDecl *Operator = cast(*Op); - if (Operator->isCopyAssignmentOperator()) { - FoundAssign = true; - const FunctionProtoType *CPT - = Operator->getType()->getAs(); - if (!CPT->hasEmptyExceptionSpec()) { - AllNoThrow = false; - break; - } - } - } - - return FoundAssign && AllNoThrow; - } - return false; - case UTT_HasNothrowCopy: - // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: - // If __has_trivial_copy (type) is true then the trait is true, else - // if type is a cv class or union type with copy constructors that are - // known not to throw an exception then the trait is true, else it is - // false. - if (T->isPODType() || T->isReferenceType()) - return true; - if (const RecordType *RT = T->getAs()) { - CXXRecordDecl *RD = cast(RT->getDecl()); - if (RD->hasTrivialCopyConstructor()) - return true; - - bool FoundConstructor = false; - bool AllNoThrow = true; - unsigned FoundTQs; - DeclarationName ConstructorName - = C.DeclarationNames.getCXXConstructorName(C.getCanonicalType(T)); - DeclContext::lookup_const_iterator Con, ConEnd; - for (llvm::tie(Con, ConEnd) = RD->lookup(ConstructorName); - Con != ConEnd; ++Con) { - CXXConstructorDecl *Constructor = cast(*Con); - if (Constructor->isCopyConstructor(FoundTQs)) { - FoundConstructor = true; - const FunctionProtoType *CPT - = Constructor->getType()->getAs(); - if (!CPT->hasEmptyExceptionSpec()) { - AllNoThrow = false; - break; - } - } - } - - return FoundConstructor && AllNoThrow; - } - return false; - case UTT_HasNothrowConstructor: - // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: - // If __has_trivial_constructor (type) is true then the trait is - // true, else if type is a cv class or union type (or array - // thereof) with a default constructor that is known not to - // throw an exception then the trait is true, else it is false. - if (T->isPODType()) - return true; - if (const RecordType *RT = C.getBaseElementType(T)->getAs()) { - CXXRecordDecl *RD = cast(RT->getDecl()); - if (RD->hasTrivialConstructor()) - return true; - - if (CXXConstructorDecl *Constructor = RD->getDefaultConstructor()) { - const FunctionProtoType *CPT - = Constructor->getType()->getAs(); - // TODO: check whether evaluating default arguments can throw. - // For now, we'll be conservative and assume that they can throw. - if (CPT->hasEmptyExceptionSpec() && CPT->getNumArgs() == 0) - return true; - } - } - return false; - case UTT_HasVirtualDestructor: - // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: - // If type is a class type with a virtual destructor ([class.dtor]) - // then the trait is true, else it is false. - if (const RecordType *Record = T->getAs()) { - CXXRecordDecl *RD = cast(Record->getDecl()); - if (CXXDestructorDecl *Destructor = RD->getDestructor()) - return Destructor->isVirtual(); - } - return false; - } -} - SourceRange CXXConstructExpr::getSourceRange() const { // FIXME: Should we know where the parentheses are, if there are any? for (std::reverse_iterator I(&Args[NumArgs]), E(&Args[0]); I!=E;++I) { diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index c2b981cdbc..fc29550a1e 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -948,7 +948,7 @@ public: } bool VisitUnaryTypeTraitExpr(const UnaryTypeTraitExpr *E) { - return Success(E->EvaluateTrait(Info.Ctx), E); + return Success(E->getValue(), E); } bool VisitChooseExpr(const ChooseExpr *E) { diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index a783737a0b..45c5fb9f0b 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -278,8 +278,7 @@ public: return 0; } Value *VisitUnaryTypeTraitExpr(const UnaryTypeTraitExpr *E) { - return llvm::ConstantInt::get(Builder.getInt1Ty(), - E->EvaluateTrait(CGF.getContext())); + return llvm::ConstantInt::get(Builder.getInt1Ty(), E->getValue()); } Value *VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *E) { diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index db287515ce..76d6f496e6 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1989,7 +1989,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, return false; } -ExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait OTT, +ExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait UTT, SourceLocation KWLoc, ParsedType Ty, SourceLocation RParen) { @@ -1998,10 +1998,210 @@ ExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait OTT, if (!TSInfo) TSInfo = Context.getTrivialTypeSourceInfo(T); - return BuildUnaryTypeTrait(OTT, KWLoc, TSInfo, RParen); + return BuildUnaryTypeTrait(UTT, KWLoc, TSInfo, RParen); } -ExprResult Sema::BuildUnaryTypeTrait(UnaryTypeTrait OTT, +static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T) { + assert(!T->isDependentType() && + "Cannot evaluate traits for dependent types."); + ASTContext &C = Self.Context; + switch(UTT) { + default: assert(false && "Unknown type trait or not implemented"); + case UTT_IsPOD: return T->isPODType(); + case UTT_IsLiteral: return T->isLiteralType(); + case UTT_IsClass: // Fallthrough + case UTT_IsUnion: + if (const RecordType *Record = T->getAs()) { + bool Union = Record->getDecl()->isUnion(); + return UTT == UTT_IsUnion ? Union : !Union; + } + return false; + case UTT_IsEnum: return T->isEnumeralType(); + case UTT_IsPolymorphic: + if (const RecordType *Record = T->getAs()) { + // Type traits are only parsed in C++, so we've got CXXRecords. + return cast(Record->getDecl())->isPolymorphic(); + } + return false; + case UTT_IsAbstract: + if (const RecordType *RT = T->getAs()) + return cast(RT->getDecl())->isAbstract(); + return false; + case UTT_IsEmpty: + if (const RecordType *Record = T->getAs()) { + return !Record->getDecl()->isUnion() + && cast(Record->getDecl())->isEmpty(); + } + return false; + case UTT_HasTrivialConstructor: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __is_pod (type) is true then the trait is true, else if type is + // a cv class or union type (or array thereof) with a trivial default + // constructor ([class.ctor]) then the trait is true, else it is false. + if (T->isPODType()) + return true; + if (const RecordType *RT = + C.getBaseElementType(T)->getAs()) + return cast(RT->getDecl())->hasTrivialConstructor(); + return false; + case UTT_HasTrivialCopy: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __is_pod (type) is true or type is a reference type then + // the trait is true, else if type is a cv class or union type + // with a trivial copy constructor ([class.copy]) then the trait + // is true, else it is false. + if (T->isPODType() || T->isReferenceType()) + return true; + if (const RecordType *RT = T->getAs()) + return cast(RT->getDecl())->hasTrivialCopyConstructor(); + return false; + case UTT_HasTrivialAssign: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If type is const qualified or is a reference type then the + // trait is false. Otherwise if __is_pod (type) is true then the + // trait is true, else if type is a cv class or union type with + // a trivial copy assignment ([class.copy]) then the trait is + // true, else it is false. + // Note: the const and reference restrictions are interesting, + // given that const and reference members don't prevent a class + // from having a trivial copy assignment operator (but do cause + // errors if the copy assignment operator is actually used, q.v. + // [class.copy]p12). + + if (C.getBaseElementType(T).isConstQualified()) + return false; + if (T->isPODType()) + return true; + if (const RecordType *RT = T->getAs()) + return cast(RT->getDecl())->hasTrivialCopyAssignment(); + return false; + case UTT_HasTrivialDestructor: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __is_pod (type) is true or type is a reference type + // then the trait is true, else if type is a cv class or union + // type (or array thereof) with a trivial destructor + // ([class.dtor]) then the trait is true, else it is + // false. + if (T->isPODType() || T->isReferenceType()) + return true; + if (const RecordType *RT = + C.getBaseElementType(T)->getAs()) + return cast(RT->getDecl())->hasTrivialDestructor(); + return false; + // TODO: Propagate nothrowness for implicitly declared special members. + case UTT_HasNothrowAssign: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If type is const qualified or is a reference type then the + // trait is false. Otherwise if __has_trivial_assign (type) + // is true then the trait is true, else if type is a cv class + // or union type with copy assignment operators that are known + // not to throw an exception then the trait is true, else it is + // false. + if (C.getBaseElementType(T).isConstQualified()) + return false; + if (T->isReferenceType()) + return false; + if (T->isPODType()) + return true; + if (const RecordType *RT = T->getAs()) { + CXXRecordDecl* RD = cast(RT->getDecl()); + if (RD->hasTrivialCopyAssignment()) + return true; + + bool FoundAssign = false; + bool AllNoThrow = true; + DeclarationName Name = C.DeclarationNames.getCXXOperatorName(OO_Equal); + DeclContext::lookup_const_iterator Op, OpEnd; + for (llvm::tie(Op, OpEnd) = RD->lookup(Name); + Op != OpEnd; ++Op) { + CXXMethodDecl *Operator = cast(*Op); + if (Operator->isCopyAssignmentOperator()) { + FoundAssign = true; + const FunctionProtoType *CPT + = Operator->getType()->getAs(); + if (!CPT->hasEmptyExceptionSpec()) { + AllNoThrow = false; + break; + } + } + } + + return FoundAssign && AllNoThrow; + } + return false; + case UTT_HasNothrowCopy: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __has_trivial_copy (type) is true then the trait is true, else + // if type is a cv class or union type with copy constructors that are + // known not to throw an exception then the trait is true, else it is + // false. + if (T->isPODType() || T->isReferenceType()) + return true; + if (const RecordType *RT = T->getAs()) { + CXXRecordDecl *RD = cast(RT->getDecl()); + if (RD->hasTrivialCopyConstructor()) + return true; + + bool FoundConstructor = false; + bool AllNoThrow = true; + unsigned FoundTQs; + DeclarationName ConstructorName + = C.DeclarationNames.getCXXConstructorName(C.getCanonicalType(T)); + DeclContext::lookup_const_iterator Con, ConEnd; + for (llvm::tie(Con, ConEnd) = RD->lookup(ConstructorName); + Con != ConEnd; ++Con) { + CXXConstructorDecl *Constructor = cast(*Con); + if (Constructor->isCopyConstructor(FoundTQs)) { + FoundConstructor = true; + const FunctionProtoType *CPT + = Constructor->getType()->getAs(); + if (!CPT->hasEmptyExceptionSpec()) { + AllNoThrow = false; + break; + } + } + } + + return FoundConstructor && AllNoThrow; + } + return false; + case UTT_HasNothrowConstructor: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __has_trivial_constructor (type) is true then the trait is + // true, else if type is a cv class or union type (or array + // thereof) with a default constructor that is known not to + // throw an exception then the trait is true, else it is false. + if (T->isPODType()) + return true; + if (const RecordType *RT = C.getBaseElementType(T)->getAs()) { + CXXRecordDecl *RD = cast(RT->getDecl()); + if (RD->hasTrivialConstructor()) + return true; + + if (CXXConstructorDecl *Constructor = RD->getDefaultConstructor()) { + const FunctionProtoType *CPT + = Constructor->getType()->getAs(); + // TODO: check whether evaluating default arguments can throw. + // For now, we'll be conservative and assume that they can throw. + if (CPT->hasEmptyExceptionSpec() && CPT->getNumArgs() == 0) + return true; + } + } + return false; + case UTT_HasVirtualDestructor: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If type is a class type with a virtual destructor ([class.dtor]) + // then the trait is true, else it is false. + if (const RecordType *Record = T->getAs()) { + CXXRecordDecl *RD = cast(Record->getDecl()); + if (CXXDestructorDecl *Destructor = RD->getDestructor()) + return Destructor->isVirtual(); + } + return false; + } +} + +ExprResult Sema::BuildUnaryTypeTrait(UnaryTypeTrait UTT, SourceLocation KWLoc, TypeSourceInfo *TSInfo, SourceLocation RParen) { @@ -2010,7 +2210,7 @@ ExprResult Sema::BuildUnaryTypeTrait(UnaryTypeTrait OTT, // According to http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html // all traits except __is_class, __is_enum and __is_union require a the type // to be complete, an array of unknown bound, or void. - if (OTT != UTT_IsClass && OTT != UTT_IsEnum && OTT != UTT_IsUnion) { + if (UTT != UTT_IsClass && UTT != UTT_IsEnum && UTT != UTT_IsUnion) { QualType E = T; if (T->isIncompleteArrayType()) E = Context.getAsArrayType(T)->getElementType(); @@ -2020,10 +2220,11 @@ ExprResult Sema::BuildUnaryTypeTrait(UnaryTypeTrait OTT, return ExprError(); } - // There is no point in eagerly computing the value. The traits are designed - // to be used from type trait templates, so Ty will be a template parameter - // 99% of the time. - return Owned(new (Context) UnaryTypeTraitExpr(KWLoc, OTT, TSInfo, + bool Value = false; + if (!T->isDependentType()) + Value = EvaluateUnaryTypeTrait(*this, UTT, T); + + return Owned(new (Context) UnaryTypeTraitExpr(KWLoc, UTT, TSInfo, Value, RParen, Context.BoolTy)); } diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index a6da3c7296..6e8ad4fc44 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -1247,6 +1247,7 @@ void ASTStmtReader::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) { void ASTStmtReader::VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E) { VisitExpr(E); E->UTT = (UnaryTypeTrait)Record[Idx++]; + E->Value = (bool)Record[Idx++]; SourceRange Range = Reader.ReadSourceRange(Record, Idx); E->Loc = Range.getBegin(); E->RParen = Range.getEnd(); diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 3701e0c146..7a5b9497a8 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -1280,6 +1280,7 @@ void ASTStmtWriter::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) { void ASTStmtWriter::VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E) { VisitExpr(E); Record.push_back(E->getTrait()); + Record.push_back(E->getValue()); Writer.AddSourceRange(E->getSourceRange(), Record); Writer.AddTypeSourceInfo(E->getQueriedTypeSourceInfo(), Record); Code = serialization::EXPR_CXX_UNARY_TYPE_TRAIT;