Support for type aliases with unexported methods in interface types
Problem Description
When using errWrap with functions that return type aliases wrapping interfaces with unexported methods, the code generator produces type expressions that cannot compile because they reference unexported method names from other packages.
Related Issue: goplus/xgo#2236
Sample Program
// In package gop/x/gopprojs
type Proj = interface{projObj()} // projObj() is unexported
func ParseAll(pattern ...string) ([]Proj, error) {
// ...
}
// In user code (main package)
import "gop/x/gopprojs"
projs := gopprojs.parseAll(pattern...)!
for proj in projs {
// ...
}
Current Behavior
The error wrapping mechanism generates code that tries to reference the unexported method:
cannot use gopprojs.ParseAll(pattern...) (value of type []interface{projObj()}) as []interface{projObj()} value in assignment
The generated closure signature contains []interface{projObj()}, but projObj() is not accessible from the main package.
Root Cause Analysis
gopprojs.Projis a type alias:type Proj = interface{projObj()}- Type aliases in Go are completely transparent - there's no
*types.Namedrepresentation - When errWrap creates a closure, it must specify return types in the closure signature
- The type system gives us
[]interface{projObj()}, but this literal form cannot be written in generated code from packagemainbecauseprojObj()is not accessible - We need a type that:
- Prints as
[]gopprojs.Projin generated code (to avoid referencing unexported methods) - Is considered identical to
[]interface{projObj()}for type checking
- Prints as
Suggested Solution
Modify gogen's AST/printer to detect when an interface type contains unexported methods from another package, and automatically use the qualified type alias name instead of the interface literal.
Implementation approach:
- When printing a type expression, check if it's an interface type
- If the interface has unexported methods, check if those methods belong to a different package
- Search the originating package's scope for a type alias that matches this interface type
- If found, use the qualified name (e.g.,
gopprojs.Proj) instead of the interface literal
This would ensure that generated code uses accessible type names while maintaining type compatibility.
Alternative Workarounds
- Change API design (breaking change): Avoid using internal interfaces as public return types
- Special case in errWrap: Detect this scenario and generate different code (e.g., avoid inline closures)
The first solution (fixing in gogen) is preferred as it solves the problem at the root cause and would benefit all code generation scenarios, not just errWrap.