When checking scopes for indirect goto, be more permissive (but still safe)
about the permitted scopes.  Specifically:
  1) Permit labels and gotos to appear after a prologue of variable initializations.
  2) Permit indirect gotos to jump out of scopes that don't require cleanup.
  3) Diagnose possible attempts to indirect-jump out of scopes that do require
     cleanup.
This requires a substantial reinvention of the algorithm for checking indirect
goto.  The current algorithm is Omega(M*N), with M = the number of unique
scopes being jumped from and N = the number of unique scopes being jumped to,
with an additional factor that is probably (worst-case) linear in the depth
of scopes.  Thus the entire thing is likely cubic given some truly bizarre
ill-formed code;  on well-formed code the additional factor collapses to
an amortized constant (when amortized over the entire function) and so
the algorithm is quadratic.  Even this requires every label to appear in
its own scope, which would be very unusual for indirect-goto code (and
extremely unlikely for well-formed code);  it is far more likely that
all labels will be in the same scope and so the algorithm becomes linear.
For such a marginal feature, I am fairly happy with this result.

(this is using JumpDiagnostic's definition of scope, where successive
variables in a block appear in their own scope)

llvm-svn: 103536
diff --git a/clang/test/SemaCXX/scope-check.cpp b/clang/test/SemaCXX/scope-check.cpp
new file mode 100644
index 0000000..cef64f6
--- /dev/null
+++ b/clang/test/SemaCXX/scope-check.cpp
@@ -0,0 +1,123 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -fblocks %s -Wno-unreachable-code
+
+namespace test0 {
+  struct D { ~D(); };
+
+  int f(bool b) {
+    if (b) {
+      D d;
+      goto end;
+    }
+
+  end:
+    return 1;
+  }
+}
+
+namespace test1 {
+  struct C { C(); };
+
+  int f(bool b) {
+    if (b)
+      goto foo; // expected-error {{illegal goto into protected scope}}
+    C c; // expected-note {{jump bypasses variable initialization}}
+  foo:
+    return 1;
+  }
+}
+
+namespace test2 {
+  struct C { C(); };
+
+  int f(void **ip) {
+    static void *ips[] = { &&lbl1, &&lbl2 };
+
+    C c;
+    goto *ip;
+  lbl1:
+    return 0;
+  lbl2:
+    return 1;
+  }
+}
+
+namespace test3 {
+  struct C { C(); };
+
+  int f(void **ip) {
+    static void *ips[] = { &&lbl1, &&lbl2 };
+
+    goto *ip;
+  lbl1: {
+    C c;
+    return 0;
+  }
+  lbl2:
+    return 1;
+  }
+}
+
+namespace test4 {
+  struct C { C(); };
+  struct D { ~D(); };
+
+  int f(void **ip) {
+    static void *ips[] = { &&lbl1, &&lbl2 };
+
+    C c0;
+
+    goto *ip; // expected-warning {{indirect goto might cross protected scopes}}
+    C c1; // expected-note {{jump bypasses variable initialization}}
+  lbl1: // expected-note {{possible target of indirect goto}}
+    return 0;
+  lbl2:
+    return 1;
+  }
+}
+
+namespace test5 {
+  struct C { C(); };
+  struct D { ~D(); };
+
+  int f(void **ip) {
+    static void *ips[] = { &&lbl1, &&lbl2 };
+    C c0;
+
+    goto *ip;
+  lbl1: // expected-note {{possible target of indirect goto}}
+    return 0;
+  lbl2:
+    if (ip[1]) {
+      D d; // expected-note {{jump exits scope of variable with non-trivial destructor}}
+      ip += 2;
+      goto *ip; // expected-warning {{indirect goto might cross protected scopes}}
+    }
+    return 1;
+  }
+}
+
+namespace test6 {
+  struct C { C(); };
+
+  unsigned f(unsigned s0, unsigned s1, void **ip) {
+    static void *ips[] = { &&lbl1, &&lbl2, &&lbl3, &&lbl4 };
+    C c0;
+
+    goto *ip;
+  lbl1:
+    s0++;
+    goto *++ip;
+  lbl2:
+    s0 -= s1;
+    goto *++ip;
+  lbl3: {
+    unsigned tmp = s0;
+    s0 = s1;
+    s1 = tmp;
+    goto *++ip;
+  }
+  lbl4:
+    return s0;
+  }
+}
+