diff --git a/go/types/builtins.go b/go/types/builtins.go index 3ff50fe1..787b33dc 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -340,7 +340,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) { if x.mode == invalid { goto Error } - base, _ := deref(x.typ) + base := derefStructPtr(x.typ) sel := arg.Sel.Name obj, index, indirect := LookupFieldOrMethod(base, check.pkg, arg.Sel.Name) switch obj.(type) { diff --git a/go/types/lookup.go b/go/types/lookup.go index f0298749..696d7590 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -292,7 +292,7 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { return } -// Deref dereferences typ if it is a pointer and returns its base and true. +// deref dereferences typ if it is a *Pointer and returns its base and true. // Otherwise it returns (typ, false). func deref(typ Type) (Type, bool) { if p, _ := typ.(*Pointer); p != nil { @@ -301,6 +301,17 @@ func deref(typ Type) (Type, bool) { return typ, false } +// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a +// (named or unnamed) struct and returns its base. Otherwise it returns typ. +func derefStructPtr(typ Type) Type { + if p, _ := typ.Underlying().(*Pointer); p != nil { + if _, ok := p.base.Underlying().(*Struct); ok { + return p.base + } + } + return typ +} + // concat returns the result of concatenating list and i. // The result does not share its underlying array with list. func concat(list []int, i int) []int { diff --git a/go/types/testdata/builtins.src b/go/types/testdata/builtins.src index d968e6e9..6a41c34f 100644 --- a/go/types/testdata/builtins.src +++ b/go/types/testdata/builtins.src @@ -373,6 +373,10 @@ func _Offsetof() { var y1p *S1 assert(unsafe.Offsetof(y1p.S0) == 32) + type P *S1 + var p P = y1p + assert(unsafe.Offsetof(p.S0) == 32) + var y2 S2 assert(unsafe.Offsetof(y2.S1) == 0) _ = unsafe.Offsetof(y2 /* ERROR "embedded via a pointer" */ .x)