From 789d77a87e5417c10377a9f9de07ec37c65048f2 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Thu, 17 Sep 2020 21:34:52 -0400 Subject: [PATCH] cmd/link: propagate UsedInIface through method descriptor The linker prunes methods that are not directly reachable if the receiver type is never converted to interface. A type can be converted to interface using reflection through other types. The linker already takes this into consideration but it missed the case that the intermediate is a method descriptor. Handle this case. Change-Id: I590efc5da163c326db8d43583908a2ef67f65d9d Reviewed-on: https://go-review.googlesource.com/c/go/+/255858 Trust: Cherry Zhang Run-TryBot: Cherry Zhang TryBot-Result: Go Bot Reviewed-by: Than McIntosh Reviewed-by: Jeremy Faller --- src/cmd/link/internal/ld/deadcode.go | 19 ++++++++++++ src/cmd/link/internal/ld/deadcode_test.go | 1 + .../ld/testdata/deadcode/ifacemethod3.go | 29 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 35545f950e..d2604b27a9 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -130,6 +130,19 @@ func (d *deadcodePass) flood() { } if usedInIface { methods = append(methods, methodref{src: symIdx, r: i}) + // The method descriptor is itself a type descriptor, and + // it can be used to reach other types, e.g. by using + // reflect.Type.Method(i).Type.In(j). We need to traverse + // its child types with UsedInIface set. (See also the + // comment below.) + rs := r.Sym() + if !d.ldr.AttrUsedInIface(rs) { + d.ldr.SetAttrUsedInIface(rs, true) + if d.ldr.AttrReachable(rs) { + d.ldr.SetAttrReachable(rs, false) + d.mark(rs, symIdx) + } + } } i += 2 continue @@ -215,9 +228,15 @@ func (d *deadcodePass) mark(symIdx, parent loader.Sym) { if *flagDumpDep { to := d.ldr.SymName(symIdx) if to != "" { + if d.ldr.AttrUsedInIface(symIdx) { + to += " " + } from := "_" if parent != 0 { from = d.ldr.SymName(parent) + if d.ldr.AttrUsedInIface(parent) { + from += " " + } } fmt.Printf("%s -> %s\n", from, to) } diff --git a/src/cmd/link/internal/ld/deadcode_test.go b/src/cmd/link/internal/ld/deadcode_test.go index 59122e9603..ab836dc8f8 100644 --- a/src/cmd/link/internal/ld/deadcode_test.go +++ b/src/cmd/link/internal/ld/deadcode_test.go @@ -32,6 +32,7 @@ func TestDeadcode(t *testing.T) { {"typedesc", "", "type.main.T"}, {"ifacemethod", "", "main.T.M"}, {"ifacemethod2", "main.T.M", ""}, + {"ifacemethod3", "main.S.M", ""}, } for _, test := range tests { test := test diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go new file mode 100644 index 0000000000..9a8dfbce5f --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go @@ -0,0 +1,29 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Like ifacemethod2.go, this tests that a method *is* live +// if the type is "indirectly" converted to an interface +// using reflection with a method descriptor as intermediate. + +package main + +import "reflect" + +type S int + +func (s S) M() { println("S.M") } + +type I interface { M() } + +type T float64 + +func (t T) F(s S) {} + +func main() { + var t T + ft := reflect.TypeOf(t).Method(0).Type + at := ft.In(1) + v := reflect.New(at).Elem() + v.Interface().(I).M() +}