gogen icon indicating copy to clipboard operation
gogen copied to clipboard

Support for type aliases with unexported methods in interface types

Open xgopilot[bot] opened this issue 1 month ago • 0 comments

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

  1. gopprojs.Proj is a type alias: type Proj = interface{projObj()}
  2. Type aliases in Go are completely transparent - there's no *types.Named representation
  3. When errWrap creates a closure, it must specify return types in the closure signature
  4. The type system gives us []interface{projObj()}, but this literal form cannot be written in generated code from package main because projObj() is not accessible
  5. We need a type that:
    • Prints as []gopprojs.Proj in generated code (to avoid referencing unexported methods)
    • Is considered identical to []interface{projObj()} for type checking

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:

  1. When printing a type expression, check if it's an interface type
  2. If the interface has unexported methods, check if those methods belong to a different package
  3. Search the originating package's scope for a type alias that matches this interface type
  4. 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

  1. Change API design (breaking change): Avoid using internal interfaces as public return types
  2. 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.

xgopilot[bot] avatar Nov 17 '25 23:11 xgopilot[bot]