]> granicus.if.org Git - clang/commitdiff
When implicitly declaring operators new, new[], delete, and delete[],
authorDouglas Gregor <dgregor@apple.com>
Tue, 15 Sep 2009 22:30:29 +0000 (22:30 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 15 Sep 2009 22:30:29 +0000 (22:30 +0000)
give them the appropriate exception specifications. This,
unfortunately, requires us to maintain and/or implicitly generate
handles to namespace "std" and the class "std::bad_alloc". However,
every other approach I've come up with was more hackish, and this
standard requirement itself is quite the hack.

Fixes PR4829.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81939 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/Sema.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExprCXX.cpp
test/CXX/basic/basic.stc/basic.stc.dynamic/p2-nodef.cpp [new file with mode: 0644]
test/CXX/basic/basic.stc/basic.stc.dynamic/p2.cpp [new file with mode: 0644]

index 5a31296781fc912244dd14164628c4de3b23f89e..3f734105d8affcb9027b06034332b2a9536345d0 100644 (file)
@@ -193,11 +193,11 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
     Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
     ExternalSource(0), CurContext(0), PreDeclaratorDC(0),
     CurBlock(0), PackContext(0), IdResolver(pp.getLangOptions()),
+    StdNamespace(0), StdBadAlloc(0),
     GlobalNewDeleteDeclared(false), ExprEvalContext(PotentiallyEvaluated),
     CompleteTranslationUnit(CompleteTranslationUnit),
     NumSFINAEErrors(0), CurrentInstantiationScope(0) {
 
-  StdNamespace = 0;
   TUScope = 0;
   if (getLangOptions().CPlusPlus)
     FieldCollector.reset(new CXXFieldCollector());
index 84e73a6fecf8d89711a1f383b22e1e047d101a87..ff99475212126f32fa4e76062e01c1e4b56ad818 100644 (file)
@@ -305,10 +305,13 @@ public:
   /// 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
+  /// \brief The C++ "std" namespace, where the standard library resides.
   NamespaceDecl *StdNamespace;
 
+  /// \brief The C++ "std::bad_alloc" class, which is defined by the C++
+  /// standard library.
+  CXXRecordDecl *StdBadAlloc;
+  
   /// A flag to remember whether the implicit forms of operator new and delete
   /// have been declared.
   bool GlobalNewDeleteDeclared;
@@ -1337,8 +1340,6 @@ public:
   void WarnConflictingTypedMethods(ObjCMethodDecl *ImpMethod,
                                    ObjCMethodDecl *IntfMethod);
 
-  NamespaceDecl *GetStdNamespace();
-
   bool isPropertyReadonly(ObjCPropertyDecl *PropertyDecl,
                           ObjCInterfaceDecl *IDecl);
 
index c7bd31160a91920d0fe3de1f6cad5317116ac292..696c10a9410caeee9ff836b4a03faca249062255 100644 (file)
@@ -521,18 +521,6 @@ NamedDecl *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) {
-    IdentifierInfo *StdIdent = &PP.getIdentifierTable().get("std");
-    DeclContext *Global = Context.getTranslationUnitDecl();
-    Decl *Std = LookupQualifiedName(Global, StdIdent, LookupNamespaceName);
-    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
@@ -3990,7 +3978,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
   DeclContext *SearchDC = CurContext;
   DeclContext *DC = CurContext;
   NamedDecl *PrevDecl = 0;
-
+  bool isStdBadAlloc = false;
   bool Invalid = false;
 
   if (Name && SS.isNotEmpty()) {
@@ -4067,6 +4055,19 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
     PrevDecl = 0;
   }
 
+  if (getLangOptions().CPlusPlus && Name && DC && StdNamespace &&
+      DC->Equals(StdNamespace) && Name->isStr("bad_alloc")) {
+    // This is a declaration of or a reference to "std::bad_alloc".
+    isStdBadAlloc = true;
+    
+    if (!PrevDecl && StdBadAlloc) {
+      // std::bad_alloc has been implicitly declared (but made invisible to
+      // name lookup). Fill in this implicit declaration as the previous 
+      // declaration, so that the declarations get chained appropriately.
+      PrevDecl = StdBadAlloc;
+    }
+  }
+      
   if (PrevDecl) {
     // Check whether the previous declaration is usable.
     (void)DiagnoseUseOfDecl(PrevDecl, NameLoc);
@@ -4253,11 +4254,14 @@ CreateNewDecl:
 
     // FIXME: Tag decls should be chained to any simultaneous vardecls, e.g.:
     // struct X { int A; } D;    D should chain to X.
-    if (getLangOptions().CPlusPlus)
+    if (getLangOptions().CPlusPlus) {
       // FIXME: Look for a way to use RecordDecl for simple structs.
       New = CXXRecordDecl::Create(Context, Kind, SearchDC, Loc, Name, KWLoc,
                                   cast_or_null<CXXRecordDecl>(PrevDecl));
-    else
+      
+      if (isStdBadAlloc && (!StdBadAlloc || StdBadAlloc->isImplicit()))
+        StdBadAlloc = cast<CXXRecordDecl>(New);
+    } else
       New = RecordDecl::Create(Context, Kind, SearchDC, Loc, Name, KWLoc,
                                cast_or_null<RecordDecl>(PrevDecl));
   }
index 9e497782507828c6230fc5bfae9453c9742a014b..a971b8a281b572aa6b750bf3b5690220079f07e8 100644 (file)
@@ -2374,6 +2374,21 @@ Sema::DeclPtrTy Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
       Diag(PrevDecl->getLocation(), diag::note_previous_definition);
       Namespc->setInvalidDecl();
       // Continue on to push Namespc as current DeclContext and return it.
+    } else if (II->isStr("std") && 
+               CurContext->getLookupContext()->isTranslationUnit()) {
+      // This is the first "real" definition of the namespace "std", so update
+      // our cache of the "std" namespace to point at this definition.
+      if (StdNamespace) {
+        // We had already defined a dummy namespace "std". Link this new 
+        // namespace definition to the dummy namespace "std".
+        StdNamespace->setNextNamespace(Namespc);
+        StdNamespace->setLocation(IdentLoc);
+        Namespc->setOriginalNamespace(StdNamespace->getOriginalNamespace());
+      }
+      
+      // Make our StdNamespace cache point at the first real definition of the
+      // "std" namespace.
+      StdNamespace = Namespc;
     }
 
     PushOnScopeChains(Namespc, DeclRegionScope);
index 0054af3493b693b0b7d1bc9fcbc1aaa7ba38bacd..503e318afa908835ca552335d827bf8b62867211 100644 (file)
@@ -61,8 +61,7 @@ Sema::ActOnCXXOperatorFunctionIdExpr(Scope *S, SourceLocation OperatorLoc,
 Action::OwningExprResult
 Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc,
                      bool isType, void *TyOrExpr, SourceLocation RParenLoc) {
-  NamespaceDecl *StdNs = GetStdNamespace();
-  if (!StdNs)
+  if (!StdNamespace)
     return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
 
   if (isType)
@@ -70,7 +69,8 @@ Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc,
     TyOrExpr = GetTypeFromParser(TyOrExpr).getAsOpaquePtr();
 
   IdentifierInfo *TypeInfoII = &PP.getIdentifierTable().get("type_info");
-  Decl *TypeInfoDecl = LookupQualifiedName(StdNs, TypeInfoII, LookupTagName);
+  Decl *TypeInfoDecl = LookupQualifiedName(StdNamespace, TypeInfoII, 
+                                           LookupTagName);
   RecordDecl *TypeInfoRecordDecl = dyn_cast_or_null<RecordDecl>(TypeInfoDecl);
   if (!TypeInfoRecordDecl)
     return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
@@ -661,12 +661,49 @@ bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range,
 void Sema::DeclareGlobalNewDelete() {
   if (GlobalNewDeleteDeclared)
     return;
+  
+  // C++ [basic.std.dynamic]p2:
+  //   [...] The following allocation and deallocation functions (18.4) are 
+  //   implicitly declared in global scope in each translation unit of a 
+  //   program
+  //   
+  //     void* operator new(std::size_t) throw(std::bad_alloc);
+  //     void* operator new[](std::size_t) throw(std::bad_alloc); 
+  //     void  operator delete(void*) throw(); 
+  //     void  operator delete[](void*) throw();
+  //
+  //   These implicit declarations introduce only the function names operator 
+  //   new, operator new[], operator delete, operator delete[].
+  //
+  // Here, we need to refer to std::bad_alloc, so we will implicitly declare
+  // "std" or "bad_alloc" as necessary to form the exception specification.
+  // However, we do not make these implicit declarations visible to name
+  // lookup.
+  if (!StdNamespace) {
+    // The "std" namespace has not yet been defined, so build one implicitly.
+    StdNamespace = NamespaceDecl::Create(Context, 
+                                         Context.getTranslationUnitDecl(),
+                                         SourceLocation(),
+                                         &PP.getIdentifierTable().get("std"));
+    StdNamespace->setImplicit(true);
+  }
+  
+  if (!StdBadAlloc) {
+    // The "std::bad_alloc" class has not yet been declared, so build it
+    // implicitly.
+    StdBadAlloc = CXXRecordDecl::Create(Context, TagDecl::TK_class, 
+                                        StdNamespace, 
+                                        SourceLocation(), 
+                                      &PP.getIdentifierTable().get("bad_alloc"), 
+                                        SourceLocation(), 0);
+    StdBadAlloc->setImplicit(true);
+  }
+  
   GlobalNewDeleteDeclared = true;
 
   QualType VoidPtr = Context.getPointerType(Context.VoidTy);
   QualType SizeT = Context.getSizeType();
 
-  // FIXME: Exception specifications are not added.
   DeclareGlobalAllocationFunction(
       Context.DeclarationNames.getCXXOperatorName(OO_New),
       VoidPtr, SizeT);
@@ -700,7 +737,19 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
     }
   }
 
-  QualType FnType = Context.getFunctionType(Return, &Argument, 1, false, 0);
+  QualType BadAllocType;
+  bool HasBadAllocExceptionSpec 
+    = (Name.getCXXOverloadedOperator() == OO_New ||
+       Name.getCXXOverloadedOperator() == OO_Array_New);
+  if (HasBadAllocExceptionSpec) {
+    assert(StdBadAlloc && "Must have std::bad_alloc declared");
+    BadAllocType = Context.getTypeDeclType(StdBadAlloc);
+  }
+  
+  QualType FnType = Context.getFunctionType(Return, &Argument, 1, false, 0,
+                                            true, false,
+                                            HasBadAllocExceptionSpec? 1 : 0,
+                                            &BadAllocType);
   FunctionDecl *Alloc =
     FunctionDecl::Create(Context, GlobalCtx, SourceLocation(), Name,
                          FnType, /*DInfo=*/0, FunctionDecl::None, false, true);
diff --git a/test/CXX/basic/basic.stc/basic.stc.dynamic/p2-nodef.cpp b/test/CXX/basic/basic.stc/basic.stc.dynamic/p2-nodef.cpp
new file mode 100644 (file)
index 0000000..b6b3e24
--- /dev/null
@@ -0,0 +1,7 @@
+// RUN: clang -fsyntax-only -verify %s
+
+int *use_new(int N) {
+  return new int [N];
+}
+
+int std = 17;
diff --git a/test/CXX/basic/basic.stc/basic.stc.dynamic/p2.cpp b/test/CXX/basic/basic.stc/basic.stc.dynamic/p2.cpp
new file mode 100644 (file)
index 0000000..f3499e4
--- /dev/null
@@ -0,0 +1,25 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+int *use_new(int N) {
+  if (N == 1)
+    return new int;
+  
+  return new int [N];
+}
+
+void use_delete(int* ip, int N) {
+  if (N == 1)
+    delete ip;
+  else
+    delete [] ip;
+}
+
+namespace std {
+  class bad_alloc { };
+  
+  typedef __SIZE_TYPE__ size_t;
+}
+
+void* operator new(std::size_t) throw(std::bad_alloc); 
+void* operator new[](std::size_t) throw(std::bad_alloc); 
+void operator delete(void*) throw(); 
+void operator delete[](void*) throw();