diff --git a/internal/clang/assign.go b/internal/clang/assign.go index 01ddca78..44d6ce6b 100644 --- a/internal/clang/assign.go +++ b/internal/clang/assign.go @@ -107,6 +107,32 @@ func (g *Generator) emitDefine(stmt *ast.AssignStmt) { typ := def.Type() ct := g.mapCType(stmt, typ) + // Shadowing in the initializer: `w := w.f()` where the RHS `w` + // refers to an outer variable of the same name. In Go the new + // variable is not in scope within its own initializer, but in C + // it would be, so emit the RHS into a temp (which still sees the + // outer binding) before declaring the new variable. + if g.identShadowedInExpr(ident, def, stmt.Rhs[i]) { + g.state.tempCount++ + tmp := fmt.Sprintf("_shadow_%s_%d", ident.Name, g.state.tempCount) + if ct.IsArray() { + fmt.Fprintf(w, "%s%s;\n", g.indent(), ct.Decl(tmp)) + fmt.Fprintf(w, "%smemcpy(%s, ", g.indent(), tmp) + g.emitExpr(stmt.Rhs[i]) + fmt.Fprintf(w, ", sizeof(%s));\n", tmp) + fmt.Fprintf(w, "%s%s;\n", g.indent(), ct.Decl(ident.Name)) + fmt.Fprintf(w, "%smemcpy(%s, %s, sizeof(%s));\n", + g.indent(), ident.Name, tmp, ident.Name) + } else { + fmt.Fprintf(w, "%s%s = ", g.indent(), ct.Decl(tmp)) + g.emitExpr(stmt.Rhs[i]) + fmt.Fprintf(w, ";\n") + fmt.Fprintf(w, "%s%s = %s;\n", g.indent(), ct.Decl(ident.Name), tmp) + } + i++ + continue + } + if ct.IsArray() { // Arrays can't be grouped with other variables. if _, isLit := stmt.Rhs[i].(*ast.CompositeLit); isLit { @@ -243,6 +269,25 @@ func (g *Generator) emitAssign(stmt *ast.AssignStmt) { // collectIdents returns the set of identifier names in the given expressions. // The blank identifier is excluded. +// identShadowedInExpr reports whether expr references an object with the +// same name as the newly-declared lhs, but distinct from def. This detects +// `x := x...` style shadowing where the RHS uses the outer binding. +func (g *Generator) identShadowedInExpr(lhs *ast.Ident, def types.Object, expr ast.Expr) bool { + found := false + ast.Inspect(expr, func(n ast.Node) bool { + id, ok := n.(*ast.Ident) + if !ok || id.Name != lhs.Name { + return true + } + if use := g.types.Uses[id]; use != nil && use != def { + found = true + return false + } + return true + }) + return found +} + func collectIdents(exprs ...ast.Expr) map[string]bool { names := map[string]bool{} for _, expr := range exprs { diff --git a/testdata/lang/variables/dst/main.c b/testdata/lang/variables/dst/main.c index 1b0c609f..98080d09 100644 --- a/testdata/lang/variables/dst/main.c +++ b/testdata/lang/variables/dst/main.c @@ -205,5 +205,26 @@ int main(void) { so_panic("multiple assignment failed"); } } + { + // Shadowing in the initializer: the RHS refers to the outer + // binding, not the variable being declared. + person p = (person){.age = 42}; + { + so_int _shadow_p_1 = p.age + 1; + so_int p = _shadow_p_1; + if (p != 43) { + so_panic("shadow scalar failed"); + } + } + so_int n = 7; + { + so_int _shadow_n_2 = n * 2; + so_int n = _shadow_n_2; + if (n != 14) { + so_panic("shadow int failed"); + } + } + (void)p; + } return 0; } diff --git a/testdata/lang/variables/src/main.go b/testdata/lang/variables/src/main.go index a64af065..e225ca8e 100644 --- a/testdata/lang/variables/src/main.go +++ b/testdata/lang/variables/src/main.go @@ -190,4 +190,23 @@ func main() { panic("multiple assignment failed") } } + { + // Shadowing in the initializer: the RHS refers to the outer + // binding, not the variable being declared. + p := person{age: 42} + { + p := p.age + 1 + if p != 43 { + panic("shadow scalar failed") + } + } + n := 7 + { + n := n * 2 + if n != 14 { + panic("shadow int failed") + } + } + _ = p + } }