From 57fdc8a4382164955c7b30d09f4ce46fc7e67659 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 26 Apr 2010 22:37:10 +0000 Subject: [PATCH] Improve source-location information in a C++ typeid (type) expression by using TypeSourceInfo, cleaning up the representation somewhat. Teach getTypeOperand() to strip references and cv-qualifiers, providing the semantic view of the type without requiring any extra storage (the unmodified type remains within the TypeSourceInfo). This fixes a bug found by Boost's call_traits test. Finally, clean up semantic analysis, by splitting the ActOnCXXTypeid routine into ActOnCXXTypeId (the parser action) and two BuildCXXTypeId functions, which perform the semantic analysis for typeid(type) and typeid(expression), respectively. We now perform less work at template instantiation time (we don't look for std::type_info again) and can give better diagnostics. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@102393 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ExprCXX.h | 44 ++++++----- lib/AST/ExprCXX.cpp | 13 ++- lib/Sema/Sema.h | 9 +++ lib/Sema/SemaExprCXX.cpp | 152 ++++++++++++++++++++---------------- lib/Sema/TreeTransform.h | 43 +++++----- test/SemaCXX/typeid-ref.cpp | 12 +++ test/SemaCXX/typeid.cpp | 1 - 7 files changed, 159 insertions(+), 115 deletions(-) create mode 100644 test/SemaCXX/typeid-ref.cpp diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index fe6fdb7c35..d66642c1ad 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -298,37 +298,41 @@ public: /// This represents code like @c typeid(int) or @c typeid(*objPtr) class CXXTypeidExpr : public Expr { private: - bool isTypeOp : 1; - union { - void *Ty; - Stmt *Ex; - } Operand; + llvm::PointerUnion Operand; SourceRange Range; public: - CXXTypeidExpr(bool isTypeOp, void *op, QualType Ty, const SourceRange r) : - Expr(CXXTypeidExprClass, Ty, + CXXTypeidExpr(QualType Ty, TypeSourceInfo *Operand, SourceRange R) + : Expr(CXXTypeidExprClass, Ty, + // typeid is never type-dependent (C++ [temp.dep.expr]p4) + false, + // typeid is value-dependent if the type or expression are dependent + Operand->getType()->isDependentType()), + Operand(Operand), Range(R) { } + + CXXTypeidExpr(QualType Ty, Expr *Operand, SourceRange R) + : Expr(CXXTypeidExprClass, Ty, // typeid is never type-dependent (C++ [temp.dep.expr]p4) false, // typeid is value-dependent if the type or expression are dependent - (isTypeOp ? QualType::getFromOpaquePtr(op)->isDependentType() - : static_cast(op)->isValueDependent())), - isTypeOp(isTypeOp), Range(r) { - if (isTypeOp) - Operand.Ty = op; - else - // op was an Expr*, so cast it back to that to be safe - Operand.Ex = static_cast(op); - } + Operand->isTypeDependent() || Operand->isValueDependent()), + Operand(Operand), Range(R) { } - bool isTypeOperand() const { return isTypeOp; } - QualType getTypeOperand() const { + bool isTypeOperand() const { return Operand.is(); } + + /// \brief Retrieves the type operand of this typeid() expression after + /// various required adjustments (removing reference types, cv-qualifiers). + QualType getTypeOperand() const; + + /// \brief Retrieve source information for the type operand. + TypeSourceInfo *getTypeOperandSourceInfo() const { assert(isTypeOperand() && "Cannot call getTypeOperand for typeid(expr)"); - return QualType::getFromOpaquePtr(Operand.Ty); + return Operand.get(); } + Expr* getExprOperand() const { assert(!isTypeOperand() && "Cannot call getExprOperand for typeid(type)"); - return static_cast(Operand.Ex); + return static_cast(Operand.get()); } virtual SourceRange getSourceRange() const { diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index c19fd834ea..5f908096bb 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -18,16 +18,25 @@ #include "clang/AST/TypeLoc.h" using namespace clang; + //===----------------------------------------------------------------------===// // Child Iterators for iterating over subexpressions/substatements //===----------------------------------------------------------------------===// +QualType CXXTypeidExpr::getTypeOperand() const { + assert(isTypeOperand() && "Cannot call getTypeOperand for typeid(expr)"); + return Operand.get()->getType().getNonReferenceType() + .getUnqualifiedType(); +} + // CXXTypeidExpr - has child iterators if the operand is an expression Stmt::child_iterator CXXTypeidExpr::child_begin() { - return isTypeOperand() ? child_iterator() : &Operand.Ex; + return isTypeOperand() ? child_iterator() + : reinterpret_cast(&Operand); } Stmt::child_iterator CXXTypeidExpr::child_end() { - return isTypeOperand() ? child_iterator() : &Operand.Ex+1; + return isTypeOperand() ? child_iterator() + : reinterpret_cast(&Operand) + 1; } // CXXBoolLiteralExpr diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 5eb95714dc..3ea9bd0afa 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2192,6 +2192,15 @@ public: SourceRange AngleBrackets, SourceRange Parens); + OwningExprResult BuildCXXTypeId(QualType TypeInfoType, + SourceLocation TypeidLoc, + TypeSourceInfo *Operand, + SourceLocation RParenLoc); + OwningExprResult BuildCXXTypeId(QualType TypeInfoType, + SourceLocation TypeidLoc, + ExprArg Operand, + SourceLocation RParenLoc); + /// ActOnCXXTypeid - Parse typeid( something ). virtual OwningExprResult ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc, bool isType, diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 84466e1d3e..7c324235ca 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -273,89 +273,107 @@ Action::TypeTy *Sema::getDestructorName(SourceLocation TildeLoc, return 0; } -/// ActOnCXXTypeidOfType - Parse typeid( type-id ). +/// \brief Build a C++ typeid expression with a type operand. +Sema::OwningExprResult Sema::BuildCXXTypeId(QualType TypeInfoType, + SourceLocation TypeidLoc, + TypeSourceInfo *Operand, + SourceLocation RParenLoc) { + // C++ [expr.typeid]p4: + // The top-level cv-qualifiers of the lvalue expression or the type-id + // that is the operand of typeid are always ignored. + // If the type of the type-id is a class type or a reference to a class + // type, the class shall be completely-defined. + QualType T = Operand->getType().getNonReferenceType(); + if (T->getAs() && + RequireCompleteType(TypeidLoc, T, diag::err_incomplete_typeid)) + return ExprError(); + + return Owned(new (Context) CXXTypeidExpr(TypeInfoType.withConst(), + Operand, + SourceRange(TypeidLoc, RParenLoc))); +} + +/// \brief Build a C++ typeid expression with an expression operand. +Sema::OwningExprResult Sema::BuildCXXTypeId(QualType TypeInfoType, + SourceLocation TypeidLoc, + ExprArg Operand, + SourceLocation RParenLoc) { + bool isUnevaluatedOperand = true; + Expr *E = static_cast(Operand.get()); + if (E && !E->isTypeDependent()) { + QualType T = E->getType(); + if (const RecordType *RecordT = T->getAs()) { + CXXRecordDecl *RecordD = cast(RecordT->getDecl()); + // C++ [expr.typeid]p3: + // [...] If the type of the expression is a class type, the class + // shall be completely-defined. + if (RequireCompleteType(TypeidLoc, T, diag::err_incomplete_typeid)) + return ExprError(); + + // C++ [expr.typeid]p3: + // When typeid is applied to an expression other than an lvalue of a + // polymorphic class type [...] [the] expression is an unevaluated + // operand. [...] + if (RecordD->isPolymorphic() && E->isLvalue(Context) == Expr::LV_Valid) + isUnevaluatedOperand = false; + } + + // C++ [expr.typeid]p4: + // [...] If the type of the type-id is a reference to a possibly + // cv-qualified type, the result of the typeid expression refers to a + // std::type_info object representing the cv-unqualified referenced + // type. + if (T.hasQualifiers()) { + ImpCastExprToType(E, T.getUnqualifiedType(), CastExpr::CK_NoOp, + E->isLvalue(Context)); + Operand.release(); + Operand = Owned(E); + } + } + + // If this is an unevaluated operand, clear out the set of + // declaration references we have been computing and eliminate any + // temporaries introduced in its computation. + if (isUnevaluatedOperand) + ExprEvalContexts.back().Context = Unevaluated; + + return Owned(new (Context) CXXTypeidExpr(TypeInfoType.withConst(), + Operand.takeAs(), + SourceRange(TypeidLoc, RParenLoc))); +} + +/// ActOnCXXTypeidOfType - Parse typeid( type-id ) or typeid (expression); Action::OwningExprResult Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc, bool isType, void *TyOrExpr, SourceLocation RParenLoc) { + // Find the std::type_info type. if (!StdNamespace) return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid)); - if (isType) { - // C++ [expr.typeid]p4: - // The top-level cv-qualifiers of the lvalue expression or the type-id - // that is the operand of typeid are always ignored. - // FIXME: Preserve type source info. - // FIXME: Preserve the type before we stripped the cv-qualifiers? - QualType T = GetTypeFromParser(TyOrExpr); - if (T.isNull()) - return ExprError(); - - // C++ [expr.typeid]p4: - // If the type of the type-id is a class type or a reference to a class - // type, the class shall be completely-defined. - QualType CheckT = T; - if (const ReferenceType *RefType = CheckT->getAs()) - CheckT = RefType->getPointeeType(); - - if (CheckT->getAs() && - RequireCompleteType(OpLoc, CheckT, diag::err_incomplete_typeid)) - return ExprError(); - - TyOrExpr = T.getUnqualifiedType().getAsOpaquePtr(); - } - IdentifierInfo *TypeInfoII = &PP.getIdentifierTable().get("type_info"); LookupResult R(*this, TypeInfoII, SourceLocation(), LookupTagName); LookupQualifiedName(R, StdNamespace); RecordDecl *TypeInfoRecordDecl = R.getAsSingle(); if (!TypeInfoRecordDecl) return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid)); - + QualType TypeInfoType = Context.getTypeDeclType(TypeInfoRecordDecl); + + if (isType) { + // The operand is a type; handle it as such. + TypeSourceInfo *TInfo = 0; + QualType T = GetTypeFromParser(TyOrExpr, &TInfo); + if (T.isNull()) + return ExprError(); + + if (!TInfo) + TInfo = Context.getTrivialTypeSourceInfo(T, OpLoc); - if (!isType) { - bool isUnevaluatedOperand = true; - Expr *E = static_cast(TyOrExpr); - if (E && !E->isTypeDependent()) { - QualType T = E->getType(); - if (const RecordType *RecordT = T->getAs()) { - CXXRecordDecl *RecordD = cast(RecordT->getDecl()); - // C++ [expr.typeid]p3: - // [...] If the type of the expression is a class type, the class - // shall be completely-defined. - if (RequireCompleteType(OpLoc, T, diag::err_incomplete_typeid)) - return ExprError(); - - // C++ [expr.typeid]p3: - // When typeid is applied to an expression other than an lvalue of a - // polymorphic class type [...] [the] expression is an unevaluated - // operand. [...] - if (RecordD->isPolymorphic() && E->isLvalue(Context) == Expr::LV_Valid) - isUnevaluatedOperand = false; - } - - // C++ [expr.typeid]p4: - // [...] If the type of the type-id is a reference to a possibly - // cv-qualified type, the result of the typeid expression refers to a - // std::type_info object representing the cv-unqualified referenced - // type. - if (T.hasQualifiers()) { - ImpCastExprToType(E, T.getUnqualifiedType(), CastExpr::CK_NoOp, - E->isLvalue(Context)); - TyOrExpr = E; - } - } - - // If this is an unevaluated operand, clear out the set of - // declaration references we have been computing and eliminate any - // temporaries introduced in its computation. - if (isUnevaluatedOperand) - ExprEvalContexts.back().Context = Unevaluated; + return BuildCXXTypeId(TypeInfoType, OpLoc, TInfo, RParenLoc); } - return Owned(new (Context) CXXTypeidExpr(isType, TyOrExpr, - TypeInfoType.withConst(), - SourceRange(OpLoc, RParenLoc))); + // The operand is an expression. + return BuildCXXTypeId(TypeInfoType, OpLoc, Owned((Expr*)TyOrExpr), RParenLoc); } /// ActOnCXXBoolLiteral - Parse {true,false} literals. diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index cded16cd60..f80747b69f 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1499,30 +1499,24 @@ public: /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - OwningExprResult RebuildCXXTypeidExpr(SourceLocation TypeidLoc, - SourceLocation LParenLoc, - QualType T, + OwningExprResult RebuildCXXTypeidExpr(QualType TypeInfoType, + SourceLocation TypeidLoc, + TypeSourceInfo *Operand, SourceLocation RParenLoc) { - return getSema().ActOnCXXTypeid(TypeidLoc, LParenLoc, true, - T.getAsOpaquePtr(), RParenLoc); + return getSema().BuildCXXTypeId(TypeInfoType, TypeidLoc, Operand, + RParenLoc); } /// \brief Build a new C++ typeid(expr) expression. /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - OwningExprResult RebuildCXXTypeidExpr(SourceLocation TypeidLoc, - SourceLocation LParenLoc, + OwningExprResult RebuildCXXTypeidExpr(QualType TypeInfoType, + SourceLocation TypeidLoc, ExprArg Operand, SourceLocation RParenLoc) { - OwningExprResult Result - = getSema().ActOnCXXTypeid(TypeidLoc, LParenLoc, false, Operand.get(), - RParenLoc); - if (Result.isInvalid()) - return getSema().ExprError(); - - Operand.release(); // FIXME: since ActOnCXXTypeid silently took ownership - return move(Result); + return getSema().BuildCXXTypeId(TypeInfoType, TypeidLoc, move(Operand), + RParenLoc); } /// \brief Build a new C++ "this" expression. @@ -4943,19 +4937,18 @@ template Sema::OwningExprResult TreeTransform::TransformCXXTypeidExpr(CXXTypeidExpr *E) { if (E->isTypeOperand()) { - TemporaryBase Rebase(*this, /*FIXME*/E->getLocStart(), DeclarationName()); - - QualType T = getDerived().TransformType(E->getTypeOperand()); - if (T.isNull()) + TypeSourceInfo *TInfo + = getDerived().TransformType(E->getTypeOperandSourceInfo()); + if (!TInfo) return SemaRef.ExprError(); if (!getDerived().AlwaysRebuild() && - T == E->getTypeOperand()) + TInfo == E->getTypeOperandSourceInfo()) return SemaRef.Owned(E->Retain()); - return getDerived().RebuildCXXTypeidExpr(E->getLocStart(), - /*FIXME:*/E->getLocStart(), - T, + return getDerived().RebuildCXXTypeidExpr(E->getType(), + E->getLocStart(), + TInfo, E->getLocEnd()); } @@ -4973,8 +4966,8 @@ TreeTransform::TransformCXXTypeidExpr(CXXTypeidExpr *E) { SubExpr.get() == E->getExprOperand()) return SemaRef.Owned(E->Retain()); - return getDerived().RebuildCXXTypeidExpr(E->getLocStart(), - /*FIXME:*/E->getLocStart(), + return getDerived().RebuildCXXTypeidExpr(E->getType(), + E->getLocStart(), move(SubExpr), E->getLocEnd()); } diff --git a/test/SemaCXX/typeid-ref.cpp b/test/SemaCXX/typeid-ref.cpp new file mode 100644 index 0000000000..da0016970b --- /dev/null +++ b/test/SemaCXX/typeid-ref.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s +namespace std { + class type_info; +} + +struct X { }; + +void f() { + // CHECK: @_ZTS1X = weak_odr constant + // CHECK: @_ZTI1X = weak_odr constant + (void)typeid(X&); +} diff --git a/test/SemaCXX/typeid.cpp b/test/SemaCXX/typeid.cpp index 0e78ff46a6..8db7db5074 100644 --- a/test/SemaCXX/typeid.cpp +++ b/test/SemaCXX/typeid.cpp @@ -5,7 +5,6 @@ void f() (void)typeid(int); // expected-error {{error: you need to include before using the 'typeid' operator}} } -// FIXME: This should really include , but we don't have that yet. namespace std { class type_info; } -- 2.40.0