]> granicus.if.org Git - clang/commitdiff
Implement C++ 'typeid' parsing and sema.
authorSebastian Redl <sebastian.redl@getdesigned.at>
Tue, 11 Nov 2008 11:37:55 +0000 (11:37 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Tue, 11 Nov 2008 11:37:55 +0000 (11:37 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59042 91177308-0d34-0410-b5e6-96231b3b80d8

20 files changed:
include/clang/AST/ExprCXX.h
include/clang/AST/StmtNodes.def
include/clang/Basic/DiagnosticKinds.def
include/clang/Parse/Action.h
include/clang/Parse/Parser.h
lib/AST/Expr.cpp
lib/AST/ExprCXX.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtSerialization.cpp
lib/Parse/ParseExpr.cpp
lib/Parse/ParseExprCXX.cpp
lib/Sema/IdentifierResolver.cpp
lib/Sema/IdentifierResolver.h
lib/Sema/Sema.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaExprCXX.cpp
test/Parser/cxx-typeid.cpp [new file with mode: 0644]
test/SemaCXX/typeid.cpp [new file with mode: 0644]
www/cxx_status.html

index 73ef6160a66283b0de9273f2db7095f4e8284cd2..37f892ce3dab48115bb0cfc75bec399395354628 100644 (file)
@@ -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<Expr*>(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:
index 7b60fcf675635fb85875866ee8cdfdd304fdbcf7..2b814a0e4b0f84a1a81158f4c8fccf9cd2242045 100644 (file)
@@ -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)
index 59c05fc20d29c734f1cb2d9d64664c62a334222b..8d821e4362b56eab3925f3aa75b43112b294235b 100644 (file)
@@ -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 <typeinfo> 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,
index 038c8583f81ef060fefa26a230575b6668780b43..d1ab26f6937448e3f62c1141a005acbd27199f7e 100644 (file)
@@ -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;
index 4071172743a14066ebed80784624a1f0d4d7ca32..9eefe55cb962fabf3c02090eab57a3233afc2d86 100644 (file)
@@ -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();
index ce0856033d6e7b92d30ab11244cd5325ba3726d9..86a2f2902e95668bbe462d44d38038f3f165cc01 100644 (file)
@@ -437,6 +437,9 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
     if (cast<ExplicitCastExpr>(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:
index 0eead3ca55264997f9e109071f9e4c2097d8fa88..ff97e688b8dcf2c1b6615405c480c5b02ef826f3 100644 (file)
@@ -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() { 
index 420bdbdee90fe2a05ce50d141f4a7779accec05d..cc861cc6b4e484f08a49a077bd0f6fb01307c4fe 100644 (file)
@@ -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");
 }
index 09aaceffda4f0e6a72ff497f4496324204ed009d..398288157691fb79f1c5374724a1868a919e8b01 100644 (file)
@@ -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<Expr>(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);
index e8758e15f10eced609ebb63ded1f18d552b0c2a8..df55bf79fcaec5ca5f2e2254fc4d148c1a947727 100644 (file)
@@ -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.
index a1fd565822dec49be6485e4663283595bb1d028b..692e35df93de8f73334d366822878b6cf2cf1dda 100644 (file)
@@ -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]
index 8375720e55cd8718c5194bee7be80b3703589530..82f95bf1082ee55ed622b3a473a53ebf57c2df87 100644 (file)
@@ -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");
 
index c661145d89f6d76c7381799336ef1365ab063e5e..e76bec6ee2508e27b760dc5071c917b76c299e72 100644 (file)
@@ -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<TranslationUnitDecl>(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'.
index 532be59cd4ddf5f7e83d7c045af5047380f8c7e3..9c8d0c8bcb414ed348bd86e166102fe7de6c800b 100644 (file)
@@ -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());
index 3b187f2099d9923d674a5c245c2dcf061465c01a..7f72928064a1b73e61fa177052bc9188cb374457 100644 (file)
@@ -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);
 
index 879a79b633aec91c7d042f28c5c9b4cab4fa91de..b595ad7f317ae70e1bddf3cca13a2d14feccb360 100644 (file)
@@ -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<NamespaceDecl>(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.
index a6a62a9e3775fd90c8bcfb5251d4b5ead3fff0e5..e420ce59916769868805232b49bdc50c4ee66a56 100644 (file)
 #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<RecordDecl>(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/test/Parser/cxx-typeid.cpp b/test/Parser/cxx-typeid.cpp
new file mode 100644 (file)
index 0000000..1a6ffdb
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: clang -fsyntax-only -verify %s
+
+// FIXME: This should really include <typeinfo>, 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/test/SemaCXX/typeid.cpp b/test/SemaCXX/typeid.cpp
new file mode 100644 (file)
index 0000000..80a70a7
--- /dev/null
@@ -0,0 +1,16 @@
+// RUN: clang -fsyntax-only -verify %s
+
+void f()
+{
+  (void)typeid(int); // expected-error {{error: you need to include <typeinfo> before using the 'typeid' operator}}
+}
+
+// FIXME: This should really include <typeinfo>, but we don't have that yet.
+namespace std {
+  class type_info;
+}
+
+void g()
+{
+  (void)typeid(int);
+}
index 586cb2652b9126dadff8f061cd93c9eb28efba72..f4e3402584d455f50aa6598a6cca9603368af13c 100644 (file)
@@ -466,9 +466,9 @@ welcome!</p>
 </tr>\r
 <tr>\r
   <td>&nbsp;&nbsp;&nbsp;&nbsp;5.2.8 [expr.typeid]</td>\r
-  <td></td>\r
-  <td></td>\r
-  <td></td>\r
+  <td class="complete" align="center">&#x2713;</td>  \r
+  <td class="complete" align="center">&#x2713;</td>  \r
+  <td class="complete" align="center">&#x2713;</td>  \r
   <td></td>\r
   <td></td>\r
 </tr>\r