]> granicus.if.org Git - clang/commitdiff
Promote a warning on ill-formed code (redeclaration missing an exception
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 30 Sep 2015 00:48:50 +0000 (00:48 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 30 Sep 2015 00:48:50 +0000 (00:48 +0000)
specification) to an error. No compiler other than Clang seems to allow this,
and it doesn't seem like a useful thing to accept as an extension in general.

The current behavior was added for PR5957, where the problem was specifically
related to mismatches of the exception specification on the implicitly-declared
global operator new and delete. To retain that workaround, we downgrade the
error to an ExtWarn when the declaration is of a replaceable global allocation
function.

Now that this is an error, stop trying (and failing) to recover from a missing
computed noexcept specification. That recovery didn't work, and led to crashes
in code like the added testcase.

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

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaExceptionSpec.cpp
test/CXX/drs/dr1xx.cpp
test/CXX/drs/dr5xx.cpp
test/CXX/except/except.spec/p3.cpp
test/CXX/except/except.spec/p4.cpp
test/CXX/except/except.spec/p5-pointers.cpp
test/FixIt/fixit.cpp
test/Misc/warning-flags.c
test/SemaCXX/exception-spec.cpp [new file with mode: 0644]

index 162f6998093f672887ec1316aab873837dc9f2d9..63343b0382df3903d235a65845e3f6836af6f772 100644 (file)
@@ -1153,7 +1153,10 @@ def err_incompatible_exception_specs : Error<
   "target exception specification is not superset of source">;
 def err_deep_exception_specs_differ : Error<
   "exception specifications of %select{return|argument}0 types differ">;
-def warn_missing_exception_specification : Warning<
+def ext_missing_exception_specification : ExtWarn<
+  "%0 is missing exception specification '%1'">,
+  InGroup<DiagGroup<"missing-exception-spec">>;
+def err_missing_exception_specification : Error<
   "%0 is missing exception specification '%1'">;
 def err_noexcept_needs_constant_expression : Error<
   "argument to noexcept specifier must be a constant expression">;
index 6182ad934e899aa0e7ca5c842d8ac623490626d6..8e1c88458e3d79bc9744a80204a5f1c20c355e74 100644 (file)
@@ -270,16 +270,31 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   FunctionProtoType::ExceptionSpecInfo ESI = OldProto->getExceptionSpecType();
   if (ESI.Type == EST_Dynamic) {
     ESI.Exceptions = OldProto->exceptions();
-  } else if (ESI.Type == EST_ComputedNoexcept) {
-    // FIXME: We can't just take the expression from the old prototype. It
-    // likely contains references to the old prototype's parameters.
   }
 
-  // Update the type of the function with the appropriate exception
-  // specification.
-  New->setType(Context.getFunctionType(
-      NewProto->getReturnType(), NewProto->getParamTypes(),
-      NewProto->getExtProtoInfo().withExceptionSpec(ESI)));
+  if (ESI.Type == EST_ComputedNoexcept) {
+    // For computed noexcept, we can't just take the expression from the old
+    // prototype. It likely contains references to the old prototype's
+    // parameters.
+    New->setInvalidDecl();
+  } else {
+    // Update the type of the function with the appropriate exception
+    // specification.
+    New->setType(Context.getFunctionType(
+        NewProto->getReturnType(), NewProto->getParamTypes(),
+        NewProto->getExtProtoInfo().withExceptionSpec(ESI)));
+  }
+
+  // Allow missing exception specifications in redeclarations as an extension,
+  // when declaring a replaceable global allocation function.
+  if (New->isReplaceableGlobalAllocationFunction() &&
+      ESI.Type != EST_ComputedNoexcept) {
+    DiagID = diag::ext_missing_exception_specification;
+    ReturnValueOnError = false;
+  } else {
+    DiagID = diag::err_missing_exception_specification;
+    ReturnValueOnError = true;
+  }
 
   // Warn about the lack of exception specification.
   SmallString<128> ExceptionSpecString;
@@ -322,17 +337,18 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   SourceLocation FixItLoc;
   if (TypeSourceInfo *TSInfo = New->getTypeSourceInfo()) {
     TypeLoc TL = TSInfo->getTypeLoc().IgnoreParens();
-    if (FunctionTypeLoc FTLoc = TL.getAs<FunctionTypeLoc>())
-      FixItLoc = getLocForEndOfToken(FTLoc.getLocalRangeEnd());
+    // FIXME: Preserve enough information so that we can produce a correct fixit
+    // location when there is a trailing return type.
+    if (auto FTLoc = TL.getAs<FunctionProtoTypeLoc>())
+      if (!FTLoc.getTypePtr()->hasTrailingReturn())
+        FixItLoc = getLocForEndOfToken(FTLoc.getLocalRangeEnd());
   }
 
   if (FixItLoc.isInvalid())
-    Diag(New->getLocation(), diag::warn_missing_exception_specification)
+    Diag(New->getLocation(), DiagID)
       << New << OS.str();
   else {
-    // FIXME: This will get more complicated with C++0x
-    // late-specified return types.
-    Diag(New->getLocation(), diag::warn_missing_exception_specification)
+    Diag(New->getLocation(), DiagID)
       << New << OS.str()
       << FixItHint::CreateInsertion(FixItLoc, " " + OS.str().str());
   }
@@ -340,7 +356,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   if (!Old->getLocation().isInvalid())
     Diag(Old->getLocation(), diag::note_previous_declaration);
 
-  return false;
+  return ReturnValueOnError;
 }
 
 /// CheckEquivalentExceptionSpec - Check if the two types have equivalent
index d8d9307a5ebb0b3a5c7f4ff722de13f3771edc68..377bfc94ffa2cc625553ceff4535fbabf98b30d2 100644 (file)
@@ -234,7 +234,7 @@ namespace dr125 {
     friend dr125_A::dr125_B (::dr125_C)(); // ok
     friend dr125_A (::dr125_B::dr125_C)(); // ok
     friend dr125_A::dr125_B::dr125_C(); // expected-error {{did you mean the constructor name 'dr125_B'?}}
-    // expected-warning@-1 {{missing exception specification}}
+    // expected-error@-1 {{missing exception specification}}
 #if __cplusplus >= 201103L
     // expected-error@-3 {{follows constexpr declaration}} expected-note@-10 {{here}}
 #endif
index 5bf085f52204ad73d064555795ff5c2e0be8bf56..0cf67e6b8c24bf9bbcdc715e6baf3f2e89aea76b 100644 (file)
@@ -7,7 +7,7 @@
 // pointing at the implicit operator new. We can't match such a diagnostic
 // with -verify.
 __extension__ typedef __SIZE_TYPE__ size_t;
-void *operator new(size_t); // expected-warning 0-1{{missing exception spec}} expected-note{{candidate}}
+void *operator new(size_t); // expected-error 0-1{{missing exception spec}} expected-note{{candidate}}
 
 namespace dr500 { // dr500: dup 372
   class D;
index d77aea40602c9533dab1db1f095e84d51b3469c6..03f1d7626c0d17c08fb8da098e54bac7a2dd3e5f 100644 (file)
@@ -24,9 +24,9 @@ extern void (*r4)() throw(...);
 extern void (*r5)() throw(int); // expected-note {{previous declaration}}
 extern void (*r5)(); // expected-error {{exception specification in declaration does not match}}
 
-// For functions, we accept this with a warning.
+// throw(int) and no spec are not compatible
 extern void f5() throw(int); // expected-note {{previous declaration}}
-extern void f5(); // expected-warning {{missing exception specification}}
+extern void f5(); // expected-error {{missing exception specification}}
 
 // Different types are not compatible.
 extern void (*r7)() throw(int); // expected-note {{previous declaration}}
index 8d1b75fbdd6d677f126ddb8550ad0cd7e75d04c3..04b2bd9bf28b4c26f0da34886dc0c693585f8fe4 100644 (file)
@@ -19,7 +19,7 @@ struct T {
   void operator delete(void*) noexcept; // expected-note {{here}}
 };
 
-void T::a() {} // expected-warning {{missing exception specification 'noexcept'}}
+void T::a() {} // expected-error {{missing exception specification 'noexcept'}}
 T::~T() {} // expected-warning {{function previously declared with an explicit exception specification redeclared with an implicit exception specification}}
 void T::operator delete(void*) {} // expected-warning {{function previously declared with an explicit exception specification redeclared with an implicit exception specification}}
 
index f855520aa24d0827832d5b88a5daf4c6a5487291..fe4a264587f5a18ba7fdba6880344b173f007b72 100644 (file)
@@ -73,7 +73,7 @@ void fnptrs()
 // Member function stuff
 
 struct Str1 { void f() throw(int); }; // expected-note {{previous declaration}}
-void Str1::f() // expected-warning {{missing exception specification}}
+void Str1::f() // expected-error {{missing exception specification}}
 {
 }
 
index 512713aa529fc753b72dfd2209f81d1cab1eb2f3..686cc23d6354910da63f04aaffabc79741484f79 100644 (file)
@@ -45,7 +45,7 @@ class B : public A {
 };
 
 void f() throw(); // expected-note{{previous}}
-void f(); // expected-warning{{missing exception specification}}
+void f(); // expected-error{{missing exception specification}}
 
 namespace rdar7853795 {
   struct A {
index f61e966612b99f609d40167e1c10d60b3831e89e..bd97c4ae80846c653014c16485638f1b944d5b66 100644 (file)
@@ -18,7 +18,7 @@ This test serves two purposes:
 
 The list of warnings below should NEVER grow.  It should gradually shrink to 0.
 
-CHECK: Warnings without flags (90):
+CHECK: Warnings without flags (89):
 CHECK-NEXT:   ext_excess_initializers
 CHECK-NEXT:   ext_excess_initializers_in_char_array_initializer
 CHECK-NEXT:   ext_expected_semi_decl_list
@@ -80,7 +80,6 @@ CHECK-NEXT:   warn_maynot_respond
 CHECK-NEXT:   warn_method_param_redefinition
 CHECK-NEXT:   warn_missing_case_for_condition
 CHECK-NEXT:   warn_missing_dependent_template_keyword
-CHECK-NEXT:   warn_missing_exception_specification
 CHECK-NEXT:   warn_missing_whitespace_after_macro_name
 CHECK-NEXT:   warn_mt_message
 CHECK-NEXT:   warn_no_constructor_for_refconst
diff --git a/test/SemaCXX/exception-spec.cpp b/test/SemaCXX/exception-spec.cpp
new file mode 100644 (file)
index 0000000..f301a63
--- /dev/null
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -fcxx-exceptions -std=c++11 %s
+
+namespace MissingOnTemplate {
+  template<typename T> void foo(T) noexcept(true); // expected-note {{previous}}
+  template<typename T> void foo(T); // expected-error {{missing exception specification 'noexcept(true)'}}
+  void test() { foo(0); }
+}