From c470476420249d85e6a82275e90d3a8f62312fa1 Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Tue, 11 Nov 2008 11:37:55 +0000 Subject: Implement C++ 'typeid' parsing and sema. llvm-svn: 59042 --- clang/include/clang/AST/ExprCXX.h | 41 +++++++++++++++++++++++ clang/include/clang/AST/StmtNodes.def | 13 ++++---- clang/include/clang/Basic/DiagnosticKinds.def | 4 +++ clang/include/clang/Parse/Action.h | 7 ++++ clang/include/clang/Parse/Parser.h | 4 +++ clang/lib/AST/Expr.cpp | 3 ++ clang/lib/AST/ExprCXX.cpp | 7 ++++ clang/lib/AST/StmtPrinter.cpp | 10 ++++++ clang/lib/AST/StmtSerialization.cpp | 28 ++++++++++++++++ clang/lib/Parse/ParseExpr.cpp | 8 ++++- clang/lib/Parse/ParseExprCXX.cpp | 48 +++++++++++++++++++++++++++ clang/lib/Sema/IdentifierResolver.cpp | 2 +- clang/lib/Sema/IdentifierResolver.h | 6 ++-- clang/lib/Sema/Sema.cpp | 4 +++ clang/lib/Sema/Sema.h | 17 +++++++++- clang/lib/Sema/SemaDecl.cpp | 15 ++++++++- clang/lib/Sema/SemaExprCXX.cpp | 28 ++++++++++++++++ clang/test/Parser/cxx-typeid.cpp | 13 ++++++++ clang/test/SemaCXX/typeid.cpp | 16 +++++++++ clang/www/cxx_status.html | 6 ++-- 20 files changed, 264 insertions(+), 16 deletions(-) create mode 100644 clang/test/Parser/cxx-typeid.cpp create mode 100644 clang/test/SemaCXX/typeid.cpp diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 73ef6160a66..37f892ce3da 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -154,6 +154,47 @@ public: virtual child_iterator child_end(); }; +/// CXXTypeidExpr - A C++ @c typeid expression (C++ [expr.typeid]), which gets +/// the type_info that corresponds to the supplied type, or the (possibly +/// dynamic) type of the supplied expression. +/// +/// This represents code like @c typeid(int) or @c typeid(*objPtr) +class CXXTypeidExpr : public Expr { +private: + bool isTypeOp : 1; + void *Operand; + SourceRange Range; + +public: + CXXTypeidExpr(bool isTypeOp, void *op, QualType Ty, const SourceRange r) : + Expr(CXXTypeidExprClass, Ty), isTypeOp(isTypeOp), Operand(op), Range(r) {} + + bool isTypeOperand() const { return isTypeOp; } + QualType getTypeOperand() const { + assert(isTypeOperand() && "Cannot call getTypeOperand for typeid(expr)"); + return QualType::getFromOpaquePtr(Operand); + } + Expr* getExprOperand() const { + assert(!isTypeOperand() && "Cannot call getExprOperand for typeid(type)"); + return static_cast(Operand); + } + + virtual SourceRange getSourceRange() const { + return Range; + } + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXTypeidExprClass; + } + static bool classof(const CXXTypeidExpr *) { return true; } + + // Iterators + virtual child_iterator child_begin(); + virtual child_iterator child_end(); + + virtual void EmitImpl(llvm::Serializer& S) const; + static CXXTypeidExpr* CreateImpl(llvm::Deserializer& D, ASTContext& C); +}; + /// CXXThisExpr - Represents the "this" expression in C++, which is a /// pointer to the object on which the current member function is /// executing (C++ [expr.prim]p3). Example: diff --git a/clang/include/clang/AST/StmtNodes.def b/clang/include/clang/AST/StmtNodes.def index 7b60fcf6756..2b814a0e4b0 100644 --- a/clang/include/clang/AST/StmtNodes.def +++ b/clang/include/clang/AST/StmtNodes.def @@ -96,12 +96,13 @@ STMT(62, CXXDynamicCastExpr , CXXNamedCastExpr) STMT(63, CXXReinterpretCastExpr , CXXNamedCastExpr) STMT(64, CXXConstCastExpr , CXXNamedCastExpr) STMT(65, CXXFunctionalCastExpr , Expr) -STMT(66, CXXBoolLiteralExpr , Expr) -STMT(67, CXXThisExpr , Expr) -STMT(68, CXXThrowExpr , Expr) -STMT(69, CXXDefaultArgExpr , Expr) -STMT(70, CXXZeroInitValueExpr , Expr) -STMT(71, CXXConditionDeclExpr , DeclRefExpr) +STMT(66, CXXTypeidExpr , Expr) +STMT(67, CXXBoolLiteralExpr , Expr) +STMT(68, CXXThisExpr , Expr) +STMT(69, CXXThrowExpr , Expr) +STMT(70, CXXDefaultArgExpr , Expr) +STMT(71, CXXZeroInitValueExpr , Expr) +STMT(72, CXXConditionDeclExpr , DeclRefExpr) // Obj-C Expressions. STMT(80, ObjCStringLiteral , Expr) diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index 59c05fc20d2..8d821e4362b 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -1157,6 +1157,10 @@ DIAG(err_ambiguous_base_to_derived_cast, ERROR, DIAG(err_static_downcast_via_virtual, ERROR, "cannot cast '%0' to '%1' via virtual base '%2'") +// Other C++ expressions +DIAG(err_need_header_before_typeid, ERROR, + "you need to include before using the 'typeid' operator") + DIAG(err_invalid_use_of_function_type, ERROR, "a function type is not allowed here") DIAG(err_invalid_use_of_array_type, ERROR, diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index 038c8583f81..d1ab26f6937 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -676,6 +676,13 @@ public: return 0; } + /// ActOnCXXTypeidOfType - Parse typeid( type-id ). + virtual ExprResult ActOnCXXTypeid(SourceLocation OpLoc, + SourceLocation LParenLoc, bool isType, + void *TyOrExpr, SourceLocation RParenLoc) { + return 0; + } + /// ActOnCXXThis - Parse the C++ 'this' pointer. virtual ExprResult ActOnCXXThis(SourceLocation ThisLoc) { return 0; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 4071172743a..9eefe55cb96 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -504,6 +504,10 @@ private: // C++ 5.2p1: C++ Casts ExprResult ParseCXXCasts(); + //===--------------------------------------------------------------------===// + // C++ 5.2p1: C++ Type Identification + ExprResult ParseCXXTypeid(); + //===--------------------------------------------------------------------===// // C++ 9.3.2: C++ 'this' pointer ExprResult ParseCXXThis(); diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index ce0856033d6..86a2f2902e9 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -437,6 +437,9 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { if (cast(this)->getTypeAsWritten()->isReferenceType()) return LV_Valid; break; + case CXXTypeidExprClass: + // C++ 5.2.8p1: The result of a typeid expression is an lvalue of ... + return LV_Valid; case CXXThisExprClass: return LV_InvalidExpression; default: diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 0eead3ca552..ff97e688b8d 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -24,6 +24,13 @@ void CXXConditionDeclExpr::Destroy(ASTContext& C) { // Child Iterators for iterating over subexpressions/substatements //===----------------------------------------------------------------------===// +// CXXTypeidExpr - has child iterators if the operand is an expression +Stmt::child_iterator CXXTypeidExpr::child_begin() { + return isTypeOperand() ? child_iterator() : (Stmt**)&Operand; +} +Stmt::child_iterator CXXTypeidExpr::child_end() { + return isTypeOperand() ? child_iterator() : (Stmt**)&Operand+1; +} // CXXBoolLiteralExpr Stmt::child_iterator CXXBoolLiteralExpr::child_begin() { diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 420bdbdee90..cc861cc6b4e 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -832,6 +832,16 @@ void StmtPrinter::VisitCXXConstCastExpr(CXXConstCastExpr *Node) { VisitCXXNamedCastExpr(Node); } +void StmtPrinter::VisitCXXTypeidExpr(CXXTypeidExpr *Node) { + OS << "typeid("; + if (Node->isTypeOperand()) { + OS << Node->getTypeOperand().getAsString(); + } else { + PrintExpr(Node->getExprOperand()); + } + OS << ")"; +} + void StmtPrinter::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *Node) { OS << (Node->getValue() ? "true" : "false"); } diff --git a/clang/lib/AST/StmtSerialization.cpp b/clang/lib/AST/StmtSerialization.cpp index 09aaceffda4..39828815769 100644 --- a/clang/lib/AST/StmtSerialization.cpp +++ b/clang/lib/AST/StmtSerialization.cpp @@ -216,6 +216,9 @@ Stmt* Stmt::Create(Deserializer& D, ASTContext& C) { case CXXConstCastExprClass: return CXXConstCastExpr::CreateImpl(D, C, SC); + case CXXTypeidExprClass: + return CXXTypeidExpr::CreateImpl(D, C); + case CXXThisExprClass: return CXXThisExpr::CreateImpl(D, C); @@ -1346,6 +1349,31 @@ CXXNamedCastExpr::CreateImpl(Deserializer& D, ASTContext& C, StmtClass SC) { } } +void CXXTypeidExpr::EmitImpl(llvm::Serializer& S) const { + S.Emit(getType()); + S.Emit(isTypeOperand()); + if (isTypeOperand()) { + S.Emit(getTypeOperand()); + } else { + S.EmitOwnedPtr(getExprOperand()); + } + S.Emit(Range); +} + +CXXTypeidExpr* +CXXTypeidExpr::CreateImpl(llvm::Deserializer& D, ASTContext& C) { + QualType Ty = QualType::ReadVal(D); + bool isTypeOp = D.ReadBool(); + void *Operand; + if (isTypeOp) { + Operand = QualType::ReadVal(D).getAsOpaquePtr(); + } else { + Operand = D.ReadOwnedPtr(C); + } + SourceRange Range = SourceRange::ReadVal(D); + return new CXXTypeidExpr(isTypeOp, Operand, Ty, Range); +} + void CXXThisExpr::EmitImpl(llvm::Serializer& S) const { S.Emit(getType()); S.Emit(Loc); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index e8758e15f10..df55bf79fca 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -357,7 +357,7 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, unsigned MinPrec) { /// /// primary-expression: [C99 6.5.1] /// [C99] identifier -// [C++] id-expression +/// [C++] id-expression /// constant /// string-literal /// [C++] boolean-literal [C++ 2.13.5] @@ -382,6 +382,8 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, unsigned MinPrec) { /// [C++] 'dynamic_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] /// [C++] 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] /// [C++] 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] +/// [C++] 'typeid' '(' expression ')' [C++ 5.2p1] +/// [C++] 'typeid' '(' type-id ')' [C++ 5.2p1] /// [C++] 'this' [C++ 9.3.2] /// [clang] '^' block-literal /// @@ -567,6 +569,10 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { Res = ParseCXXCasts(); // These can be followed by postfix-expr pieces. return ParsePostfixExpressionSuffix(Res); + case tok::kw_typeid: + Res = ParseCXXTypeid(); + // This can be followed by postfix-expr pieces. + return ParsePostfixExpressionSuffix(Res); case tok::kw_this: Res = ParseCXXThis(); // This can be followed by postfix-expr pieces. diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index a1fd565822d..692e35df93d 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -216,6 +216,54 @@ Parser::ExprResult Parser::ParseCXXCasts() { return Result; } +/// ParseCXXTypeid - This handles the C++ typeid expression. +/// +/// postfix-expression: [C++ 5.2p1] +/// 'typeid' '(' expression ')' +/// 'typeid' '(' type-id ')' +/// +Parser::ExprResult Parser::ParseCXXTypeid() { + assert(Tok.is(tok::kw_typeid) && "Not 'typeid'!"); + + SourceLocation OpLoc = ConsumeToken(); + SourceLocation LParenLoc = Tok.getLocation(); + SourceLocation RParenLoc; + + // typeid expressions are always parenthesized. + if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, + "typeid")) + return ExprResult(true); + + Parser::ExprResult Result; + + if (isTypeIdInParens()) { + TypeTy *Ty = ParseTypeName(); + + // Match the ')'. + MatchRHSPunctuation(tok::r_paren, LParenLoc); + + if (!Ty) + return ExprResult(true); + + Result = Actions.ActOnCXXTypeid(OpLoc, LParenLoc, /*isType=*/true, + Ty, RParenLoc); + } else { + Result = ParseExpression(); + + // Match the ')'. + if (Result.isInvalid) + SkipUntil(tok::r_paren); + else { + MatchRHSPunctuation(tok::r_paren, LParenLoc); + + Result = Actions.ActOnCXXTypeid(OpLoc, LParenLoc, /*isType=*/false, + Result.Val, RParenLoc); + } + } + + return Result; +} + /// ParseCXXBoolLiteral - This handles the C++ Boolean literals. /// /// boolean-literal: [C++ 2.13.5] diff --git a/clang/lib/Sema/IdentifierResolver.cpp b/clang/lib/Sema/IdentifierResolver.cpp index 8375720e55c..82f95bf1082 100644 --- a/clang/lib/Sema/IdentifierResolver.cpp +++ b/clang/lib/Sema/IdentifierResolver.cpp @@ -243,7 +243,7 @@ void IdentifierResolver::RemoveDecl(NamedDecl *D) { /// declaration context 'Ctx'. If 'LookInParentCtx' is true, it will walk the /// decls of parent declaration contexts too. IdentifierResolver::iterator -IdentifierResolver::begin(const IdentifierInfo *II, DeclContext *Ctx, +IdentifierResolver::begin(const IdentifierInfo *II, const DeclContext *Ctx, bool LookInParentCtx) { assert(Ctx && "null param passed"); diff --git a/clang/lib/Sema/IdentifierResolver.h b/clang/lib/Sema/IdentifierResolver.h index c661145d89f..e76bec6ee25 100644 --- a/clang/lib/Sema/IdentifierResolver.h +++ b/clang/lib/Sema/IdentifierResolver.h @@ -31,7 +31,7 @@ class IdentifierResolver { /// ScopedDecls, LookupContext can be used with all decls (assumes /// translation unit context for non ScopedDecls). class LookupContext { - DeclContext *Ctx; + const DeclContext *Ctx; /// TUCtx - Provides a common value for translation unit context for all /// decls. @@ -49,7 +49,7 @@ class IdentifierResolver { LookupContext(Decl *D) { Ctx = getContext(D); } - LookupContext(DeclContext *DC) { + LookupContext(const DeclContext *DC) { if (!DC || isa(DC)) Ctx = TUCtx(); else @@ -196,7 +196,7 @@ public: /// declaration context 'Ctx'. If 'LookInParentCtx' is true, it will walk the /// decls of parent declaration contexts too. /// Default for 'LookInParentCtx is true. - static iterator begin(const IdentifierInfo *II, DeclContext *Ctx, + static iterator begin(const IdentifierInfo *II, const DeclContext *Ctx, bool LookInParentCtx = true); /// end - Returns an iterator that has 'finished'. diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 532be59cd4d..9c8d0c8bcb4 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -113,6 +113,10 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer) Ident_SEL = &IT.get("SEL"); Ident_Protocol = &IT.get("Protocol"); + Ident_StdNs = &IT.get("std"); + Ident_TypeInfo = 0; + StdNamespace = 0; + TUScope = 0; if (getLangOptions().CPlusPlus) FieldCollector.reset(new CXXFieldCollector()); diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 3b187f2099d..7f72928064a 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -199,10 +199,18 @@ public: IdentifierInfo *Ident_id, *Ident_Class; // "id", "Class" IdentifierInfo *Ident_SEL, *Ident_Protocol; // "SEL", "Protocol" + /// Identifiers used by the C++ language + IdentifierInfo *Ident_StdNs; // "std" + IdentifierInfo *Ident_TypeInfo; // "type_info" - lazily created + /// Translation Unit Scope - useful to Objective-C actions that need /// to lookup file scope declarations in the "ordinary" C decl namespace. /// For example, user-defined classes, built-in "id" type, etc. Scope *TUScope; + + /// The C++ "std" namespace, where the standard library resides. Cached here + /// by GetStdNamespace + NamespaceDecl *StdNamespace; /// ObjCMethodList - a linked list of methods with different signatures. struct ObjCMethodList { @@ -450,7 +458,7 @@ public: /// More parsing and symbol table subroutines... Decl *LookupDecl(const IdentifierInfo *II, unsigned NSI, Scope *S, - DeclContext *LookupCtx = 0, + const DeclContext *LookupCtx = 0, bool enableLazyBuiltinCreation = true); ObjCInterfaceDecl *getObjCInterfaceDecl(IdentifierInfo *Id); ScopedDecl *LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID, @@ -463,6 +471,8 @@ public: void WarnUndefinedMethod(SourceLocation ImpLoc, ObjCMethodDecl *method, bool &IncompleteImpl); + + NamespaceDecl *GetStdNamespace(); /// CheckProtocolMethodDefs - This routine checks unimpletented /// methods declared in protocol, and those referenced by it. @@ -751,6 +761,11 @@ public: SourceLocation LParenLoc, ExprTy *E, SourceLocation RParenLoc); + /// ActOnCXXTypeidOfType - Parse typeid( type-id ). + virtual ExprResult ActOnCXXTypeid(SourceLocation OpLoc, + SourceLocation LParenLoc, bool isType, + void *TyOrExpr, SourceLocation RParenLoc); + //// ActOnCXXThis - Parse 'this' pointer. virtual ExprResult ActOnCXXThis(SourceLocation ThisLoc); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 879a79b633a..b595ad7f317 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -193,7 +193,8 @@ ObjCInterfaceDecl *Sema::getObjCInterfaceDecl(IdentifierInfo *Id) { /// LookupDecl - Look up the inner-most declaration in the specified /// namespace. Decl *Sema::LookupDecl(const IdentifierInfo *II, unsigned NSI, Scope *S, - DeclContext *LookupCtx, bool enableLazyBuiltinCreation) { + const DeclContext *LookupCtx, + bool enableLazyBuiltinCreation) { if (II == 0) return 0; unsigned NS = NSI; if (getLangOptions().CPlusPlus && (NS & Decl::IDNS_Ordinary)) @@ -278,6 +279,18 @@ ScopedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned bid, return New; } +/// GetStdNamespace - This method gets the C++ "std" namespace. This is where +/// everything from the standard library is defined. +NamespaceDecl *Sema::GetStdNamespace() { + if (!StdNamespace) { + DeclContext *Global = Context.getTranslationUnitDecl(); + Decl *Std = LookupDecl(Ident_StdNs, Decl::IDNS_Tag | Decl::IDNS_Ordinary, + 0, Global, /*enableLazyBuiltinCreation=*/false); + StdNamespace = dyn_cast_or_null(Std); + } + return StdNamespace; +} + /// MergeTypeDefDecl - We just parsed a typedef 'New' which has the same name /// and scope as a previous declaration 'Old'. Figure out how to resolve this /// situation, merging decls or emitting diagnostics as appropriate. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index a6a62a9e377..e420ce59916 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -19,6 +19,34 @@ #include "clang/Basic/Diagnostic.h" using namespace clang; + +/// ActOnCXXTypeidOfType - Parse typeid( type-id ). +Action::ExprResult +Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc, + bool isType, void *TyOrExpr, SourceLocation RParenLoc) { + const NamespaceDecl *StdNs = GetStdNamespace(); + if (!StdNs) { + Diag(OpLoc, diag::err_need_header_before_typeid); + return ExprResult(true); + } + if (!Ident_TypeInfo) { + Ident_TypeInfo = &PP.getIdentifierTable().get("type_info"); + } + Decl *TypeInfoDecl = LookupDecl(Ident_TypeInfo, + Decl::IDNS_Tag | Decl::IDNS_Ordinary, + 0, StdNs, /*createBuiltins=*/false); + RecordDecl *TypeInfoRecordDecl = dyn_cast_or_null(TypeInfoDecl); + if (!TypeInfoRecordDecl) { + Diag(OpLoc, diag::err_need_header_before_typeid); + return ExprResult(true); + } + + QualType TypeInfoType = Context.getTypeDeclType(TypeInfoRecordDecl); + + return new CXXTypeidExpr(isType, TyOrExpr, TypeInfoType.withConst(), + SourceRange(OpLoc, RParenLoc)); +} + /// ActOnCXXBoolLiteral - Parse {true,false} literals. Action::ExprResult Sema::ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind) { diff --git a/clang/test/Parser/cxx-typeid.cpp b/clang/test/Parser/cxx-typeid.cpp new file mode 100644 index 00000000000..1a6ffdbcd0a --- /dev/null +++ b/clang/test/Parser/cxx-typeid.cpp @@ -0,0 +1,13 @@ +// RUN: clang -fsyntax-only -verify %s + +// FIXME: This should really include , but we don't have that yet. +namespace std { + class type_info; +} + +void f() +{ + (void)typeid(int); + (void)typeid(0); + (void)typeid 1; // expected-error {{error: expected '(' after 'typeid'}} +} diff --git a/clang/test/SemaCXX/typeid.cpp b/clang/test/SemaCXX/typeid.cpp new file mode 100644 index 00000000000..80a70a7b562 --- /dev/null +++ b/clang/test/SemaCXX/typeid.cpp @@ -0,0 +1,16 @@ +// RUN: clang -fsyntax-only -verify %s + +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; +} + +void g() +{ + (void)typeid(int); +} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 586cb2652b9..f4e3402584d 100644 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -466,9 +466,9 @@ welcome!

    5.2.8 [expr.typeid] - - - + ✓ + ✓ + ✓ -- cgit v1.2.3