diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index e5ea071e86..98c9e121b0 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -154,8 +154,11 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { var v_used bool if ident != nil { if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil { - v, _ = obj.(*Var) - if v != nil { + // It's ok to mark non-local variables, but ignore variables + // from other packages to avoid potential race conditions with + // dot-imported variables. + if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg { + v = w v_used = v.used } } @@ -249,6 +252,7 @@ func (check *Checker) assignVars(lhs, rhs []ast.Expr) { l := len(lhs) get, r, commaOk := unpack(func(x *operand, i int) { check.multiExpr(x, rhs[i]) }, len(rhs), l == 2) if get == nil { + check.useLHS(lhs...) return // error reported by unpack } if l != r { diff --git a/src/go/types/call.go b/src/go/types/call.go index 345df66a8a..8fe65e41d5 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -90,15 +90,52 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind { // use type-checks each argument. // Useful to make sure expressions are evaluated // (and variables are "used") in the presence of other errors. +// The arguments may be nil. func (check *Checker) use(arg ...ast.Expr) { var x operand for _, e := range arg { - if e != nil { // be safe + // The nil check below is necessary since certain AST fields + // may legally be nil (e.g., the ast.SliceExpr.High field). + if e != nil { check.rawExpr(&x, e, nil) } } } +// useLHS is like use, but doesn't "use" top-level identifiers. +// It should be called instead of use if the arguments are +// expressions on the lhs of an assignment. +// The arguments must not be nil. +func (check *Checker) useLHS(arg ...ast.Expr) { + var x operand + for _, e := range arg { + // If the lhs is an identifier denoting a variable v, this assignment + // is not a 'use' of v. Remember current value of v.used and restore + // after evaluating the lhs via check.rawExpr. + var v *Var + var v_used bool + if ident, _ := unparen(e).(*ast.Ident); ident != nil { + // never type-check the blank name on the lhs + if ident.Name == "_" { + continue + } + if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil { + // It's ok to mark non-local variables, but ignore variables + // from other packages to avoid potential race conditions with + // dot-imported variables. + if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg { + v = w + v_used = v.used + } + } + } + check.rawExpr(&x, e, nil) + if v != nil { + v.used = v_used // restore v.used + } + } +} + // useGetter is like use, but takes a getter instead of a list of expressions. // It should be called instead of use if a getter is present to avoid repeated // evaluation of the first argument (since the getter was likely obtained via diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 7428f8f995..9b250b30e7 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -111,7 +111,11 @@ func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) { if typ != nil { t := check.typ(typ) if !isConstType(t) { - check.errorf(typ.Pos(), "invalid constant type %s", t) + // don't report an error if the type is an invalid C (defined) type + // (issue #22090) + if t.Underlying() != Typ[Invalid] { + check.errorf(typ.Pos(), "invalid constant type %s", t) + } obj.typ = Typ[Invalid] return } diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index 1292f5cec1..ab320088b0 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -731,6 +731,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { // declaration, but the post statement must not." if s, _ := s.Post.(*ast.AssignStmt); s != nil && s.Tok == token.DEFINE { check.softErrorf(s.Pos(), "cannot declare in post statement") + // Don't call useLHS here because we want to use the lhs in + // this errroneous statement so that we don't get errors about + // these lhs variables being declared but not used. check.use(s.Lhs...) // avoid follow-up errors } check.stmt(inner, s.Body) diff --git a/src/go/types/testdata/importC.src b/src/go/types/testdata/importC.src index 31436be6ad..f50f7f33d3 100644 --- a/src/go/types/testdata/importC.src +++ b/src/go/types/testdata/importC.src @@ -8,3 +8,28 @@ import "C" import _ /* ERROR cannot rename import "C" */ "C" import foo /* ERROR cannot rename import "C" */ "C" import . /* ERROR cannot rename import "C" */ "C" + +// Test cases extracted from issue #22090. + +import "unsafe" + +const _ C.int = 0xff // no error due to invalid constant type + +type T struct { + Name string + Ordinal int +} + +func f(args []T) { + var s string + for i, v := range args { + cname := C.CString(v.Name) + args[i].Ordinal = int(C.sqlite3_bind_parameter_index(s, cname)) // no error due to i not being "used" + C.free(unsafe.Pointer(cname)) + } +} + +type CType C.Type + +const _ CType = C.X // no error due to invalid constant type +const _ = C.X diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 0ab6dfdb79..92ab06b0f2 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -86,6 +86,9 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa } case *Var: + // It's ok to mark non-local variables, but ignore variables + // from other packages to avoid potential race conditions with + // dot-imported variables. if obj.pkg == check.pkg { obj.used = true }