Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/tools/go/callgraph/rta: model reflective calls soundly #61160

Closed
adonovan opened this issue Jul 3, 2023 · 1 comment
Closed

x/tools/go/callgraph/rta: model reflective calls soundly #61160

adonovan opened this issue Jul 3, 2023 · 1 comment
Assignees
Labels
FrozenDueToAge Tools This label describes issues relating to any tools in the x/tools repository.
Milestone

Comments

@adonovan
Copy link
Member

adonovan commented Jul 3, 2023

The RTA package should support an option to soundly handle reflective calls of reflectively generated function signatures.

After stumbling across some dead code in x/tools, I spent some time trying to build a dead-code detection tool based on RTA, which seems like the ideal analysis for the job. It is mostly sound with respect to dynamic calls because any time a value of a non-interface type is converted to an interface (ssa.MakeInterface), our RTA implementation assumes that not only are all the methods of the concrete type reachable through interface dynamic calls, but additionally all the types that can be obtained from the interface value through the reflect package are also "live", so their methods are in turn treated as candidates for dynamic calls.

However, there are gaps. Consider the program below, which prints "hello":

package main

import "reflect"

type T int
type U int

func (T) hello(U) { println("hello") }

func main() {
	reflect.ValueOf(T(0).hello).Call([]reflect.Value{reflect.ValueOf(U(0))})
}

To the RTA algorithm, there exist no dynamic call sites using the signature func(U). (That's the reason for the type U in the example, to avoid T.hello from coincidentally having a signature that is used for dynamic calls elsewhere, such as in the reflect package.) So, the fact that the method T.hello has its address taken in the first call to ValueOf doesn't result in any call graph edges, nor in T.hello being considered reachable.

But as we can see from the dynamic behavior, the reflective Call operator can potentially act as a call to any address-taken function or method. Calls such as this are common: they are how template expressions call back into Go through template.FuncMap, and in x/tools they are how the packagestest Expect function calls back into test code such as the methods of gopls' tests.Data. Unfortunately the invisibility of such calls to RTA causes significant portions of the application and/or tests to be considered dead code when it is very much alive.

The solution is to conservatively assume that reflect.Value.Call acts like a dynamic call to any function signature.

@adonovan adonovan self-assigned this Jul 3, 2023
@gopherbot gopherbot added the Tools This label describes issues relating to any tools in the x/tools repository. label Jul 3, 2023
@gopherbot gopherbot added this to the Unreleased milestone Jul 3, 2023
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/507737 mentions this issue: go/callgraph/rta: model reflective calls soundly

@golang golang locked and limited conversation to collaborators Jul 5, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge Tools This label describes issues relating to any tools in the x/tools repository.
Projects
None yet
Development

No branches or pull requests

2 participants