diff options
| author | Luc Van Oostenryck <luc.vanoostenryck@gmail.com> | 2020-05-21 18:23:04 +0200 |
|---|---|---|
| committer | Luc Van Oostenryck <luc.vanoostenryck@gmail.com> | 2020-05-21 18:23:04 +0200 |
| commit | 4d96f72782e076f7c4410394364aa77336c09bd2 (patch) | |
| tree | 6cc86b72f196f6b4465edb74797903f3cf4711b5 /validation | |
| parent | 18351292096b1b90ed7f3bc34459d371aa095588 (diff) | |
| parent | 384008074a8d8881fefaa4fdda330120385d1259 (diff) | |
| download | sparse-dev-4d96f72782e076f7c4410394364aa77336c09bd2.tar.gz | |
Merge branch 'bad-goto'
* warn when jumping into statement expressions
* warn when using undefined labels
* warn on defined but unused labels
It's not allowed to do a goto into an expression statement.
For example, it's not well defined what should happen if such
an expression is not otherwise reachable and/or can be optimized
away. For such situations GCC issues an error, clang doesn't
and produce a valid IR but Spare produce an invalid IR with
branches to unexisting BBs.
The goals of the patches in this series are:
*) to detect such gotos at evaluation time;
*) issue a sensible error message;
*) avoid the linearization of functions with invalid gotos.
The implementation principle behind these is to add a new kind
of scope (label_scope), one for the usual function scope of
labels one for each statement expressions. This new scope,
instead of being used as a real scope for the visibility of
labels, is used to mark where labels are defined and where
they're used.
Using this label scope as a real scope controling the
visibility of labels was quite appealing and was the initial
drive for this implementation but has the problem of inner
scope shadowing earlier occurence of labels identically
named. This is of course desired for 'normal' symbols but for
labels (which are normally visible in the whole function
and which may be used before being declared/defined)
it has the disadvantage of:
*) inhibiting the detecting of misuses once an inner scope
is closed
*) allowing several distinct labels with the same name
in a single function (this can be regarded as a feature
but __label__ at block scope should be used for this)
*) create diffrences about what is permssble or not between
sparse and GCC or clang.
Diffstat (limited to 'validation')
21 files changed, 463 insertions, 16 deletions
diff --git a/validation/__func__-scope.c b/validation/__func__-scope.c new file mode 100644 index 00000000..508a8b91 --- /dev/null +++ b/validation/__func__-scope.c @@ -0,0 +1,8 @@ +static void foo(void) +{ + const char *name = ({ __func__; }); +} +/* + * check-name: __func__'s scope + * check-command: sparse -Wall $file + */ diff --git a/validation/asm-goto-lables.c b/validation/asm-goto-labels.c index ac2bf2ad..ac2bf2ad 100644 --- a/validation/asm-goto-lables.c +++ b/validation/asm-goto-labels.c diff --git a/validation/label-asm.c b/validation/label-asm.c index 411020ac..b58d1e52 100644 --- a/validation/label-asm.c +++ b/validation/label-asm.c @@ -3,6 +3,7 @@ static void f(void) { barrier(); + goto l; l: barrier(); } diff --git a/validation/label-attr.c b/validation/label-attr.c index a82d7bc9..81c4ac3c 100644 --- a/validation/label-attr.c +++ b/validation/label-attr.c @@ -1,6 +1,6 @@ static int foo(void) { - return 0; + goto rtattr_failure; rtattr_failure: __attribute__ ((unused)) return -1; } diff --git a/validation/label-scope-cgoto.c b/validation/label-scope-cgoto.c new file mode 100644 index 00000000..1edb9948 --- /dev/null +++ b/validation/label-scope-cgoto.c @@ -0,0 +1,82 @@ +void foo(void) +{ + void *p = &&l; + { +l: ; + } + goto *p; // OK +} + +void bar(void) +{ + void *p = &&l; // KO: 'jump' inside + ({ +l: 1; + }); + goto *p; +} + +void baz(void) +{ + void *p = &&l; // KO: 'jump' inside + 0 ? 1 : ({ +l: 1; + }); + goto *p; +} + +void qux(void) +{ + void *p = &&l; // KO: 'jump' inside + removed + 1 ? 1 : ({ +l: 1; + }); + goto *p; +} + +void quz(void) +{ + void *p; + p = &&l; // KO: undeclared + goto *p; +} + +void qxu(void) +{ + void *p; + ({ +l: 1; + }); + p = &&l; // KO: 'jump' inside + goto *p; +} + +void qzu(void) +{ + void *p; + 1 ? 1 : ({ +l: 1; + }); + p = &&l; // KO: 'jump' inside + removed + goto *p; +} + + +/* + * check-name: label-scope-cgoto + * check-command: sparse -Wno-decl $file + * + * check-error-start +label-scope-cgoto.c:12:19: error: label 'l' used outside statement expression +label-scope-cgoto.c:14:1: label 'l' defined here +label-scope-cgoto.c:21:19: error: label 'l' used outside statement expression +label-scope-cgoto.c:23:1: label 'l' defined here +label-scope-cgoto.c:30:19: error: label 'l' used outside statement expression +label-scope-cgoto.c:32:1: label 'l' defined here +label-scope-cgoto.c:50:13: error: label 'l' used outside statement expression +label-scope-cgoto.c:48:1: label 'l' defined here +label-scope-cgoto.c:60:13: error: label 'l' used outside statement expression +label-scope-cgoto.c:58:1: label 'l' defined here +label-scope-cgoto.c:40:13: error: label 'l' was not declared + * check-error-end + */ diff --git a/validation/label-scope.c b/validation/label-scope.c index 7af3d916..0ffaaf4a 100644 --- a/validation/label-scope.c +++ b/validation/label-scope.c @@ -3,10 +3,7 @@ static int f(int n) __label__ n; n: return n; } -static int g(int n) -{ -n: return n; -} + /* * check-name: __label__ scope */ diff --git a/validation/label-scope1.c b/validation/label-scope1.c new file mode 100644 index 00000000..f2b1ae9b --- /dev/null +++ b/validation/label-scope1.c @@ -0,0 +1,42 @@ +static void ok_top(void) +{ + __label__ l; +l: + goto l; +} + +static void ko_undecl(void) +{ + __label__ l; + goto l; // KO: undeclared +} + +static void ok_local(void) +{ +l: + { + __label__ l; +l: + goto l; + } +goto l; +} + +static void ko_scope(void) +{ + { + __label__ l; +l: + goto l; + } +goto l; // KO: undeclared +} + +/* + * check-name: label-scope1 + * + * check-error-start +label-scope1.c:11:9: error: label 'l' was not declared +label-scope1.c:32:1: error: label 'l' was not declared + * check-error-end + */ diff --git a/validation/label-scope2.c b/validation/label-scope2.c new file mode 100644 index 00000000..44864752 --- /dev/null +++ b/validation/label-scope2.c @@ -0,0 +1,31 @@ +static void ok_lvl2(void) +{ + __label__ l; + + { + l: + goto l; + } +} + +static void ko_expr2(void) +{ + { + __label__ a; + + ({ +a: + 0; + }); + goto a; + } +} + +/* + * check-name: label-scope2 + * + * check-error-start +label-scope2.c:20:17: error: label 'a' used outside statement expression +label-scope2.c:17:1: label 'a' defined here + * check-error-end + */ diff --git a/validation/label-stmt-expr0.c b/validation/label-stmt-expr0.c new file mode 100644 index 00000000..5fc452ab --- /dev/null +++ b/validation/label-stmt-expr0.c @@ -0,0 +1,38 @@ +void aft(void) +{ + ({ +l: 1; + }); + goto l; // KO +} + +void bef(void) +{ + goto l; // KO + ({ +l: 1; + }); +} + +void lab(void) +{ + __label__ l; + ({ +l: 1; + }); + goto l; // KO +} + +/* + * check-name: label-stmt-expr0 + * check-command: sparse -Wno-decl $file + * + * check-error-start +label-stmt-expr0.c:6:9: error: label 'l' used outside statement expression +label-stmt-expr0.c:4:1: label 'l' defined here +label-stmt-expr0.c:11:9: error: label 'l' used outside statement expression +label-stmt-expr0.c:13:1: label 'l' defined here +label-stmt-expr0.c:23:9: error: label 'l' used outside statement expression +label-stmt-expr0.c:21:1: label 'l' defined here + * check-error-end + */ diff --git a/validation/label-stmt-expr1.c b/validation/label-stmt-expr1.c new file mode 100644 index 00000000..32a89aad --- /dev/null +++ b/validation/label-stmt-expr1.c @@ -0,0 +1,28 @@ +static int foo(void) +{ + goto l; + ({ +l: + 0; + }); +} + +static void bar(void) +{ + ({ +l: + 0; + }); + goto l; +} + +/* + * check-name: label-stmt-expr1 + * + * check-error-start +label-stmt-expr1.c:3:9: error: label 'l' used outside statement expression +label-stmt-expr1.c:5:1: label 'l' defined here +label-stmt-expr1.c:16:9: error: label 'l' used outside statement expression +label-stmt-expr1.c:13:1: label 'l' defined here + * check-error-end + */ diff --git a/validation/label-stmt-expr2.c b/validation/label-stmt-expr2.c new file mode 100644 index 00000000..8c54477a --- /dev/null +++ b/validation/label-stmt-expr2.c @@ -0,0 +1,46 @@ +static int foo(void) +{ + goto l; + ({ +l: + 0; + }); + goto l; +} + +static void bar(void) +{ + goto l; + goto l; + ({ +l: + 0; + }); +} + +static void baz(void) +{ + ({ +l: + 0; + }); + goto l; + goto l; +} + +/* + * check-name: label-stmt-expr2 + * + * check-error-start +label-stmt-expr2.c:3:9: error: label 'l' used outside statement expression +label-stmt-expr2.c:5:1: label 'l' defined here +label-stmt-expr2.c:8:9: error: label 'l' used outside statement expression +label-stmt-expr2.c:5:1: label 'l' defined here +label-stmt-expr2.c:13:9: error: label 'l' used outside statement expression +label-stmt-expr2.c:16:1: label 'l' defined here +label-stmt-expr2.c:27:9: error: label 'l' used outside statement expression +label-stmt-expr2.c:24:1: label 'l' defined here +label-stmt-expr2.c:28:9: error: label 'l' used outside statement expression +label-stmt-expr2.c:24:1: label 'l' defined here + * check-error-end + */ diff --git a/validation/label-unused.c b/validation/label-unused.c new file mode 100644 index 00000000..e3f255e1 --- /dev/null +++ b/validation/label-unused.c @@ -0,0 +1,29 @@ +static void foo(void) +{ +l: + return; +} + +static int bar(void) +{ + return ({ +l: + ; + 0; + }); +} + +static void baz(void) +{ +l: __attribute__((unused)); + return; +} + +/* + * check-name: label-unused + * + * check-error-start +label-unused.c:3:1: warning: unused label 'l' +label-unused.c:10:1: warning: unused label 'l' + * check-error-end + */ diff --git a/validation/linear/goto-invalid.c b/validation/linear/goto-invalid.c new file mode 100644 index 00000000..860b32a4 --- /dev/null +++ b/validation/linear/goto-invalid.c @@ -0,0 +1,18 @@ +static void foo(void) +{ + goto return; +} + +void bar(void) +{ + goto neverland; +} + +/* + * check-name: goto-invalid + * check-command: test-linearize -Wno-decl $file + * + * check-error-ignore + * check-output-ignore + * check-output-excludes: END + */ diff --git a/validation/linear/goto-stmt-expr-conditional.c b/validation/linear/goto-stmt-expr-conditional.c new file mode 100644 index 00000000..bbfcb3eb --- /dev/null +++ b/validation/linear/goto-stmt-expr-conditional.c @@ -0,0 +1,27 @@ +int t(void) +{ + goto inside; + return 1 ? 2 : ({ +inside: + return 3; + 4; + }); +} + +void f(int x, int y) +{ + 1 ? x : ({ +a: + y; + }); + goto a; +} + +/* + * check-name: goto-stmt-expr-conditional + * check-command: test-linearize -Wno-decl $file + * + * check-error-ignore + * check-output-ignore + * check-output-excludes: END + */ diff --git a/validation/linear/goto-stmt-expr-short-circuit.c b/validation/linear/goto-stmt-expr-short-circuit.c new file mode 100644 index 00000000..a5953e73 --- /dev/null +++ b/validation/linear/goto-stmt-expr-short-circuit.c @@ -0,0 +1,31 @@ +int foo(int p) +{ + goto inside; + if (0 && ({ +inside: + return 1; + 2; + })) + return 3; + return 4; +} + +int bar(int p) +{ + if (0 && ({ +inside: + return 1; + 2; + })) + return 3; + goto inside; +} + +/* + * check-name: goto-stmt-expr-short-circuit + * check-command: test-linearize -Wno-decl $file + * + * check-error-ignore + * check-output-ignore + * check-output-excludes: END + */ diff --git a/validation/linear/label-scope-cgoto.c b/validation/linear/label-scope-cgoto.c new file mode 100644 index 00000000..0eba05ae --- /dev/null +++ b/validation/linear/label-scope-cgoto.c @@ -0,0 +1,10 @@ +#include <label-scope-cgoto.c> + +/* + * check-name: linear/label-scope-cgoto + * check-command: test-linearize -Wno-decl -I. $file + * + * check-error-ignore + * check-output-ignore + * check-output-excludes: END + */ diff --git a/validation/discarded-label-statement.c b/validation/linear/label-stmt-dropped.c index b4e58ac6..74e0f2e6 100644 --- a/validation/discarded-label-statement.c +++ b/validation/linear/label-stmt-dropped.c @@ -11,11 +11,13 @@ start: r += a; r += b; + if (!r) + goto start; return r; } /* - * check-name: discarded-label-statement + * check-name: label-stmt-dropped * check-command: test-linearize $file * * check-output-ignore diff --git a/validation/label-expr.c b/validation/linear/label-stmt-expr0.c index e578ed00..ff3c0980 100644 --- a/validation/label-expr.c +++ b/validation/linear/label-stmt-expr0.c @@ -3,12 +3,12 @@ int foo(void) { int r; - r = ({ label: 1; }); + r = ({ goto label; label: 1; }); return r; } /* - * check-name: label-expr + * check-name: label-stmt-expr0 * check-command: test-linearize $file * check-output-ignore * diff --git a/validation/linear/unreachable-label0.c b/validation/linear/label-unreachable.c index 695e5cb0..a44e1211 100644 --- a/validation/linear/unreachable-label0.c +++ b/validation/linear/label-unreachable.c @@ -10,9 +10,10 @@ label: } /* - * check-name: unreachable-label0 + * check-name: label-unreachable * check-command: test-linearize $file * + * check-error-ignore * check-output-ignore * check-output-contains: ret\\. * check-output-excludes: END diff --git a/validation/nested-functions.c b/validation/nested-functions.c new file mode 100644 index 00000000..349edb5a --- /dev/null +++ b/validation/nested-functions.c @@ -0,0 +1,43 @@ +static int test_ok(int a, int b) +{ + int nested_ok(int i) + { + return i * 2; + } + return nested_ok(b); +} + +static int test_ko(int a, int b) +{ + int nested_ko(int i) + { + return i * 2 + a; + } + return nested_ko(b); +} + +static int test_inline(int a, int b) +{ + inline int nested(int i) + { + return i * 2; + } + return nested(b); +} + +static int test_inline_ko(int a, int b) +{ + inline int nested(int i) + { + return i * 2 + a; + } + return nested(b); +} + +/* + * check-name: nested-functions + * + * check-error-start +nested-functions.c:32:32: warning: unreplaced symbol 'a' + * check-error-end + */ diff --git a/validation/typeof-safe.c b/validation/typeof-safe.c index 614863fb..797c759f 100644 --- a/validation/typeof-safe.c +++ b/validation/typeof-safe.c @@ -2,22 +2,35 @@ static void test_safe(void) { - int __safe obj, *ptr; - typeof(obj) var = obj; - typeof(ptr) ptr2 = ptr; + int obj; + int __safe *ptr; + + int __safe *ptr2 = ptr; + typeof(ptr) ptr3 = ptr; typeof(*ptr) var2 = obj; - typeof(*ptr) *ptr3 = ptr; - typeof(obj) *ptr4 = ptr; + int __safe var3 = obj; + int *ptr4 = &obj; + int *ptr5 = ptr; // KO + + typeof(*ptr) sobj; + typeof(&sobj) ptr6 = &obj; + typeof(&sobj) ptr7 = ptr; // KO + obj = obj; ptr = ptr; - ptr = &obj; obj = *ptr; + ptr = (int __safe *) &obj; } /* * check-name: typeof-safe - * check-known-to-fail * * check-error-start +typeof-safe.c:13:21: warning: incorrect type in initializer (different modifiers) +typeof-safe.c:13:21: expected int *ptr5 +typeof-safe.c:13:21: got int [safe] *ptr +typeof-safe.c:17:30: warning: incorrect type in initializer (different modifiers) +typeof-safe.c:17:30: expected int *ptr7 +typeof-safe.c:17:30: got int [safe] *ptr * check-error-end */ |
