Skip to content

Commit d1fb004

Browse files
committed
clang: forbid composite literals in macro calls (#78)
1 parent aae6917 commit d1fb004

3 files changed

Lines changed: 26 additions & 133 deletions

File tree

internal/clang/expr.go

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -609,27 +609,42 @@ func (g *Generator) emitNotNil(w io.Writer, expr ast.Expr, suffixes ...string) {
609609
return
610610
}
611611
fmt.Fprint(w, "so_notnil(")
612-
g.emitMacroArg(w, expr)
612+
g.emitExpr(w, expr)
613613
fmt.Fprint(w, suffix)
614614
fmt.Fprint(w, ")")
615615
}
616616

617-
// emitMacroArg emits an argument to a function-like macro, wrapping
618-
// it in parentheses when the expression is a composite literal.
617+
// emitMacroArg emits an argument to a function-like macro.
618+
//
619+
// Composite literals reference stack-backed temporaries, so passing them to a
620+
// macro that indexes, slices, or stores them would create a use-after-scope
621+
// bug. Only a struct value literal is safe (it gets copied into the slot); it
622+
// is wrapped in parentheses so the preprocessor does not split it on the commas
623+
// of its braced initializer. Everything else fails.
619624
//
620-
// All emitted macro calls must use emitMacroArg for their arguments
621-
// to avoid preprocessor parsing issues.
625+
// All emitted macro calls must use emitMacroArg for their arguments.
622626
func (g *Generator) emitMacroArg(w io.Writer, arg ast.Expr) {
623-
if isCompositeLit(arg) {
624-
// A composite literal emits a braced initializer (e.g. (so_Slice){p, n, n})
625-
// whose commas would otherwise be misread by the preprocessor as macro
626-
// argument separators.
627-
fmt.Fprint(w, "(")
627+
// &T{...}: a pointer to a block-scoped temporary that dangles once it escapes.
628+
if u, ok := arg.(*ast.UnaryExpr); ok && u.Op == token.AND {
629+
if _, ok := u.X.(*ast.CompositeLit); ok {
630+
g.fail(arg, "cannot use composite literal here; assign it to a variable first")
631+
}
632+
}
633+
lit, ok := arg.(*ast.CompositeLit)
634+
if !ok {
635+
// Not a composite literal, emit directly.
628636
g.emitExpr(w, arg)
637+
return
638+
}
639+
// Only struct value literals are safe; they are copied into the slot.
640+
if _, ok := g.types.TypeOf(lit).Underlying().(*types.Struct); ok {
641+
fmt.Fprint(w, "(")
642+
g.emitExpr(w, lit)
629643
fmt.Fprint(w, ")")
630644
return
631645
}
632-
g.emitExpr(w, arg)
646+
// Array, slice, or map literal: references stack-backed storage.
647+
g.fail(arg, "cannot use composite literal here; assign it to a variable first")
633648
}
634649

635650
// needsVoidParens reports whether expr needs parentheses in a (void) cast.
@@ -670,20 +685,6 @@ func (g *Generator) isArrayParam(ident *ast.Ident) bool {
670685
return false
671686
}
672687

673-
// isCompositeLit reports whether expr emits a braced composite-literal
674-
// initializer at the top level.
675-
func isCompositeLit(expr ast.Expr) bool {
676-
switch e := expr.(type) {
677-
case *ast.CompositeLit:
678-
// E.g., Point{1, 2} or []int{1, 2, 3}.
679-
return true
680-
case *ast.UnaryExpr:
681-
// E.g. &Point{1, 2} or &[]int{1, 2, 3}.
682-
return isCompositeLit(e.X)
683-
}
684-
return false
685-
}
686-
687688
// isSelfParenthesized reports whether expr emits its own parentheses.
688689
func isSelfParenthesized(expr ast.Expr) bool {
689690
bin, ok := expr.(*ast.BinaryExpr)

testdata/lang/macro-args/dst/main.c

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44

55
typedef struct point point;
66

7-
// Composite literals passed to function-like C macros (len, cap, append,
8-
// copy, clear, indexing, slicing, map access) emit braced initializers whose
9-
// commas would be misread by the preprocessor as macro argument separators.
10-
// Each such argument must be wrapped in parentheses.
117
typedef struct point {
128
so_int x;
139
so_int y;
@@ -16,64 +12,18 @@ typedef struct point {
1612
// -- Implementation --
1713

1814
int main(void) {
19-
// len/cap of a slice literal.
20-
if (so_len(((so_Slice){(so_int[3]){1, 2, 3}, 3, 3})) != 3) {
21-
so_panic("len");
22-
}
23-
if (so_cap(((so_Slice){(so_int[3]){1, 2, 3}, 3, 3})) != 3) {
24-
so_panic("cap");
25-
}
26-
// index and slice of a slice literal.
27-
if (so_at(so_int, ((so_Slice){(so_int[3]){10, 20, 30}, 3, 3}), 1) != 20) {
28-
so_panic("index");
29-
}
30-
if (so_len(so_slice(so_int, ((so_Slice){(so_int[4]){1, 2, 3, 4}, 4, 4}), 1, 3)) != 2) {
31-
so_panic("slice");
32-
}
33-
if (so_len(so_slice(so_int, ((so_Slice){(so_int[4]){1, 2, 3, 4}, 4, 4}), 2, ((so_Slice){(so_int[4]){1, 2, 3, 4}, 4, 4}).len)) != 2) {
34-
so_panic("slice open-ended");
35-
}
36-
// address of an element of a slice literal.
37-
(void)&so_at(so_int, ((so_Slice){(so_int[3]){5, 6, 7}, 3, 3}), 0);
38-
// slice-to-array conversion of a literal.
39-
so_int arr[2];
40-
memcpy(arr, so_slice_array(((so_Slice){(so_int[2]){7, 8}, 2, 2}), 2), sizeof(arr));
41-
if (arr[0] != 7) {
42-
so_panic("slice-to-array");
43-
}
44-
// byte slice literal to string conversion.
45-
if (so_string_ne(so_bytes_string(((so_Slice){(so_byte[2]){'h', 'i'}, 2, 2})), so_str("hi"))) {
46-
so_panic("byte slice to string");
47-
}
48-
if (so_string_ne(so_bytes_string(((so_Slice){(so_byte[1]){(so_byte)(97)}, 1, 1})), so_str("a"))) {
49-
so_panic("byte slice to string");
50-
}
51-
// copy from a slice literal.
52-
so_Slice dst = so_make_slice(so_int, 3, 3);
53-
so_copy(so_int, dst, ((so_Slice){(so_int[3]){1, 2, 3}, 3, 3}));
54-
if (so_at(so_int, dst, 2) != 3) {
55-
so_panic("copy");
56-
}
5715
// append a composite-literal value.
5816
so_Slice pts = so_make_slice(point, 0, 2);
5917
pts = so_append(point, pts, ((point){1, 2}));
6018
if (so_at(point, pts, 0).y != 2) {
6119
so_panic("append value");
6220
}
63-
// clear a slice literal (exercises the macro; no observable effect).
64-
so_clear(so_int, ((so_Slice){(so_int[3]){1, 2, 3}, 3, 3}));
6521
// map with a composite-literal value.
6622
so_Map* mv = so_make_map(so_int, point, 1);
6723
so_map_set(so_int, point, mv, 0, ((point){3, 4}));
6824
if (so_map_get(so_int, point, mv, 0).x != 3) {
6925
so_panic("map value");
7026
}
71-
// map with a composite-literal pointer value.
72-
so_Map* mp = so_make_map(so_int, point*, 1);
73-
so_map_set(so_int, point*, mp, 1, (&(point){8, 9}));
74-
if (so_map_get(so_int, point*, mp, 1)->y != 9) {
75-
so_panic("map pointer value");
76-
}
7727
// map with a composite-literal key.
7828
so_Map* mk = so_make_map(point, so_int, 1);
7929
so_map_set(point, so_int, mk, ((point){1, 2}), 42);

testdata/lang/macro-args/src/main.go

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,24 @@
11
package main
22

3-
// Composite literals passed to function-like C macros (len, cap, append,
4-
// copy, clear, indexing, slicing, map access) emit braced initializers whose
5-
// commas would be misread by the preprocessor as macro argument separators.
6-
// Each such argument must be wrapped in parentheses.
7-
83
type point struct {
94
x, y int
105
}
116

127
func main() {
13-
// len/cap of a slice literal.
14-
if len([]int{1, 2, 3}) != 3 {
15-
panic("len")
16-
}
17-
if cap([]int{1, 2, 3}) != 3 {
18-
panic("cap")
19-
}
20-
21-
// index and slice of a slice literal.
22-
if []int{10, 20, 30}[1] != 20 {
23-
panic("index")
24-
}
25-
if len([]int{1, 2, 3, 4}[1:3]) != 2 {
26-
panic("slice")
27-
}
28-
if len([]int{1, 2, 3, 4}[2:]) != 2 {
29-
panic("slice open-ended")
30-
}
31-
32-
// address of an element of a slice literal.
33-
_ = &[]int{5, 6, 7}[0]
34-
35-
// slice-to-array conversion of a literal.
36-
arr := [2]int([]int{7, 8})
37-
if arr[0] != 7 {
38-
panic("slice-to-array")
39-
}
40-
41-
// byte slice literal to string conversion.
42-
if string([]byte{'h', 'i'}) != "hi" {
43-
panic("byte slice to string")
44-
}
45-
if string([]byte{byte(97)}) != "a" {
46-
panic("byte slice to string")
47-
}
48-
49-
// copy from a slice literal.
50-
dst := make([]int, 3)
51-
copy(dst, []int{1, 2, 3})
52-
if dst[2] != 3 {
53-
panic("copy")
54-
}
55-
568
// append a composite-literal value.
579
pts := make([]point, 0, 2)
5810
pts = append(pts, point{1, 2})
5911
if pts[0].y != 2 {
6012
panic("append value")
6113
}
6214

63-
// clear a slice literal (exercises the macro; no observable effect).
64-
clear([]int{1, 2, 3})
65-
6615
// map with a composite-literal value.
6716
mv := make(map[int]point, 1)
6817
mv[0] = point{3, 4}
6918
if mv[0].x != 3 {
7019
panic("map value")
7120
}
7221

73-
// map with a composite-literal pointer value.
74-
mp := make(map[int]*point, 1)
75-
mp[1] = &point{8, 9}
76-
if mp[1].y != 9 {
77-
panic("map pointer value")
78-
}
79-
8022
// map with a composite-literal key.
8123
mk := make(map[point]int, 1)
8224
mk[point{1, 2}] = 42

0 commit comments

Comments
 (0)