Skip to content

Conversation

@visualfc
Copy link
Member

@visualfc visualfc commented Jan 3, 2026

runtime reflect

  • reflect.FuncOf fix prototype ( func() is closure)
  • reflect.storeRcvr check by runtime.DirectIfaceData
  • reflect.methodReceiver check rcvrtype to abi.Pointer to match ifn call
  • reflect.Value.call check return type alignment
  • reflect.Value.call check return by resolveIndirectValue
  • runtime: export runtime.DirectIfaceData for reflect.storeRcvr
  • ssa: detect needAbiInit moved into builder.call
	reflectFunc = map[string]none{
		"reflect.ArrayOf":            none{},
		"reflect.ChanOf":             none{},
		"reflect.FuncOf":             none{},
		"reflect.MapOf":              none{},
		"reflect.SliceOf":            none{},
		"reflect.StructOf":           none{},
		"reflect.Value.Method":       none{},
		"reflect.Value.MethodByName": none{},
	}

@gemini-code-assist
Copy link

Summary of Changes

Hello @visualfc, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses and resolves issues within the reflect package concerning the accurate handling of function types, particularly in the context of closures. It introduces a more precise mechanism for reflect.FuncOf to construct function prototypes by correctly interpreting the internal structure of closures. Additionally, it refines the reflect.Value.Pointer() and reflect.Value.UnsafePointer() methods to ensure that function code pointers are extracted accurately, specifically applying specialized logic only when the reflect.Value represents a closure. These changes collectively contribute to more robust and correct reflection behavior for functions within the Go runtime.

Highlights

  • Refined reflect.FuncOf Prototype Determination: The FuncOf method now correctly determines the prototype for function types, especially when dealing with closures, by extracting the FuncType from the underlying abi.StructType rather than a direct funcType cast.
  • Improved Function Pointer Extraction for Closures: The Pointer() and UnsafePointer() methods of reflect.Value have been updated to apply special logic for extracting the code pointer from a data block only when the Value represents a closure, ensuring accuracy for different function types.
  • Explicit unsafe.Pointer Cast: An explicit cast to unsafe.Pointer has been added when assigning the function pointer p.Tfn_ within the Method function, enhancing type safety and correctness.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the runtime reflection logic to correctly handle a new representation of function types as closures.

The main change in reflect.FuncOf correctly obtains a function type prototype by treating the type of func() as a closure struct and extracting the function type from its first field. This aligns with the updated runtime representation.

Additionally, reflect.Value.Pointer and reflect.Value.UnsafePointer are improved to differentiate between closure and non-closure function values. By adding an IsClosure() check, the code now correctly avoids dereferencing a direct function pointer, which is a good correctness fix.

The changes are consistent and seem correct. I have not found any issues in this pull request.

@xgopilot
Copy link

xgopilot bot commented Jan 3, 2026

Code Review Summary

I've completed a comprehensive review of this PR using specialized agents for code quality, performance, security, and documentation. Overall, this is a well-designed fix that properly addresses the distinction between closure types and function types in the LLGo reflection system.

Key Findings

Security: No vulnerabilities identified. The changes demonstrate proper defensive programming with type and null guards.

Performance: Negligible impact. The additional IsClosure() checks are simple bitfield operations with excellent branch prediction.

Code Quality: Clean and focused changes that follow established patterns.

Recommendations

Before merging:

  1. Add a meaningful PR description explaining what bug this fixes and why the closure structure extraction is necessary
  2. Consider adding a clarifying comment at line 309 in type.go explaining why closure extraction isn't needed for method function pointers

See inline comments for specific details.

@codecov
Copy link

codecov bot commented Jan 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.01%. Comparing base (01ee853) to head (5d7226a).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1528   +/-   ##
=======================================
  Coverage   91.01%   91.01%           
=======================================
  Files          45       45           
  Lines       11958    11959    +1     
=======================================
+ Hits        10883    10884    +1     
  Misses        899      899           
  Partials      176      176           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@visualfc visualfc changed the title [WIP] runtime: fix reflect.FuncOf prototype [WIP] runtime: fix reflect.Value.Method Jan 5, 2026
@visualfc visualfc changed the title [WIP] runtime: fix reflect.Value.Method runtime: fix reflect.Value.Method Jan 7, 2026
@visualfc visualfc force-pushed the reflect_funcof branch 2 times, most recently from d294170 to b27eb92 Compare January 7, 2026 02:22
@visualfc
Copy link
Member Author

visualfc commented Jan 7, 2026

@xgopilot

@xgopilot
Copy link

xgopilot bot commented Jan 7, 2026

Validation Summary

I've reviewed and validated the PR changes. Here's my assessment:

Changes Overview

This PR introduces several important fixes to the reflect package's method handling:

  1. reflect.FuncOf prototype fix (type.go:1246): Correctly extracts function type from closure structure instead of direct casting
  2. Closure type checking (value.go:1128, 1735): Added IsClosure() checks to Pointer() and UnsafePointer() methods to only dereference closure data blocks
  3. Pointer type checking in makeMethodValue (makefunc.go:226): Ensures proper pointer handling for method receivers
  4. Method receiver type handling (value.go:2305-2308): Adds pointer type conversion for non-pointer receiver types
  5. Return value alignment (value.go:2401-2405): Fixes multi-return value alignment based on FFI signature requirements
  6. Direct interface method handling (value.go:2736-2738): Correctly selects Tfn_ vs Ifn_ for value vs pointer receivers

Code Quality

Formatting: All code is properly formatted (verified with go fmt)
Code quality: Changes follow existing patterns and are well-structured
Demo coverage: Comprehensive test demo with 540+ lines covering various method scenarios

Testing Status

⚠️ Note: Some CI tests fail due to missing Python development dependencies (python3-embed), which is expected per CLAUDE.md documentation. The core reflect functionality changes cannot be fully validated in this environment due to runtime compatibility issues between the installed llgo binary and the current source.

Recommendation

The changes appear sound and address real issues with:

  • Closure vs function type distinction
  • Method receiver type handling
  • Multi-return value alignment
  • Direct interface method calls

The comprehensive demo program in _demo/go/reflectmethod/main.go provides extensive test coverage for the fixed scenarios.

Suggested next steps: Run the full test suite in a properly configured development environment with all dependencies installed to ensure the fixes work correctly across all scenarios.

@visualfc visualfc force-pushed the reflect_funcof branch 2 times, most recently from 69cf0c0 to ff169a9 Compare January 12, 2026 03:46
@visualfc
Copy link
Member Author

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a series of significant fixes to the reflect package, primarily focusing on the correctness of method handling and calls. The changes correctly handle method value creation for various receiver types, fix FuncOf to align with closure representation, and ensure proper alignment for multiple return values in Value.call. The introduction of resolveIndirectValue and the export of runtime.DirectIfaceData improve the handling of values stored directly in interfaces. The addition of a comprehensive test suite is a valuable contribution that validates these complex changes. The PR is of high quality and substantially improves the robustness of the reflection implementation. I have one suggestion regarding potential data corruption for small array/struct types in the new resolveIndirectValue function.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several important fixes to the reflect package, primarily targeting reflect.Value.Method and reflect.Value.Call. The changes correctly handle method receivers for both value and pointer types, ensure proper alignment and value representation for function return values, and adjust the function prototype creation to match the closure-based function representation in the runtime. Additionally, runtime.DirectIfaceData is now correctly used to handle values stored directly in interfaces. A comprehensive test suite has been added to validate these reflection features. My review found one minor opportunity for code simplification.

@visualfc visualfc force-pushed the reflect_funcof branch 3 times, most recently from e1545d7 to b5b5d33 Compare January 12, 2026 08:01
@visualfc
Copy link
Member Author

@xgopilot

@xgopilot
Copy link

xgopilot bot commented Jan 12, 2026

Code Review Complete

I've reviewed the changes in PR #1528. This PR fixes several critical issues in reflect.Value.Method and related reflection functionality. Previous reviews have already validated the core changes, so I'll focus on specific technical observations.

Summary of Changes

The PR addresses:

  1. FuncOf prototype extraction - Correctly extracts function type from closure structure
  2. IsClosure() checks - Adds proper closure detection in Pointer() and UnsafePointer()
  3. Method receiver handling - Ensures pointer conversion for non-pointer receivers
  4. Return value alignment - Fixes multi-return value alignment with FFI signature requirements
  5. Direct interface data handling - Exports DirectIfaceData for proper receiver storage
  6. SSA needAbiInit detection - Moves detection from NewFuncEx to Builder.Call

Technical Issues Identified

1. Potential Issue in resolveIndirectValue (value.go:2416-2442)

The default case at line 2434-2439 handles Array and Struct types by loading a full word:

default:
    if typ.Size_ < unsafe.Sizeof(0) {
        v.ptr = truncate(*(*unsafe.Pointer)(v.ptr), typ.Size_*8)
    } else {
        v.ptr = *(*unsafe.Pointer)(v.ptr)
    }

Issue: For small structs/arrays stored directly (< word size), loading *(*unsafe.Pointer)(v.ptr) reads a full word which may include garbage in upper bits. While truncate() masks this, it only masks by bit count, not actual struct layout.

Recommendation: Consider if this is correct for packed structs or if it needs field-by-field handling. The current approach assumes the value is stored in the low-order bits contiguously.

2. storeRcvr Logic Flow (value.go:2453-2466)

The updated logic has 4 branches:

if t.Kind() == abi.Interface {
    *(*unsafe.Pointer)(p) = ifacePtrData(iface)
} else if v.flag&flagIndir != 0 && !ifaceIndir(t) {
    *(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
} else if v.flag&flagIndir == 0 && runtime.DirectIfaceData(t) {
    *(*unsafe.Pointer)(p) = unsafe.Pointer(&v.ptr)
} else {
    *(*unsafe.Pointer)(p) = v.ptr
}

Observation: The third branch (line 2461-2462) handles the case where value is stored directly (flagIndir == 0) and needs a pointer to it. This correctly handles direct-stored small values like int8, bool, etc.

Question: Is the ordering of these conditions optimal? Consider documenting why the flagIndir == 0 && DirectIfaceData check comes after the flagIndir != 0 check.

3. makeMethodValue Changes (makefunc.go:218-220)

_, _, fn := methodReceiver(op, rcvr, int(v.flag)>>flagMethodShift)
var ptr unsafe.Pointer
storeRcvr(v, unsafe.Pointer(&ptr))

Good: This correctly uses storeRcvr with the original v (not rcvr), preserving the method receiver's flags.

Note: The order matters - methodReceiver is called with rcvr (which has method bit cleared), but storeRcvr is called with original v to capture the correct receiver value.

4. Receiver Type Pointer Conversion (value.go:2305-2307)

if rcvrtype.Kind() != abi.Pointer {
    rcvrtype = toRType(rcvrtype).ptrTo()
}

Issue: This unconditionally converts non-pointer receiver types to pointers. While this may be correct for FFI calling conventions, consider:

  • Is this always the right behavior for value receivers?
  • Should this check whether the method actually expects a pointer?
  • Could this cause issues with methods that explicitly take value receivers?

Recommendation: Add a comment explaining why all method receivers need pointer types in the FFI layer, even for value receiver methods.

Positive Aspects

Comprehensive test coverage - The 537-line demo thoroughly exercises the changes
DirectIfaceData export - Proper abstraction for direct interface data detection
SSA detection improvement - Moving needAbiInit to call site is more accurate
resolveIndirectValue - Handles type-specific conversions properly for most cases

Recommendations

  1. Add comments for the non-obvious logic in storeRcvr condition ordering
  2. Document why rcvrtype needs pointer conversion for all method calls
  3. Verify the resolveIndirectValue default case handles all struct/array layouts correctly
  4. Consider adding unit tests specifically for the edge cases (small structs, packed arrays)

Conclusion

The changes are well-structured and address real issues in the reflection system. The main concerns are around edge case handling in resolveIndirectValue and clarity around why receiver pointer conversion is always needed. These are clarification issues rather than blocking problems.

👉 Want me to re-run this review? Just type /review in the comments! For more usage examples, visit CodeAgent GitHub Repository.

@visualfc
Copy link
Member Author

@xgopilot

@xgopilot
Copy link

xgopilot bot commented Jan 13, 2026

Code Review Complete

I've reviewed the changes in PR #1528. This PR fixes several critical issues in reflect.Value.Method and related reflection functionality. The changes are generally well-structured, but I've identified some specific technical concerns that should be addressed.


Critical Issues

1. Flag Check Inconsistency in toFFIArg (value.go:2174)

Location: runtime/internal/lib/reflect/value.go:2174

The change from flagAddr to flagIndir appears correct for checking if a value is stored indirectly, but this needs verification:

case abi.Bool, abi.Int, abi.Int8, abi.Int16, abi.Int32, abi.Int64,
    abi.Uint, abi.Uint8, abi.Uint16, abi.Uint32, abi.Uint64, abi.Uintptr,
    abi.Float32, abi.Float64:
-   if v.flag&flagAddr != 0 {
+   if v.flag&flagIndir != 0 {
        return v.ptr
    } else {
        return unsafe.Pointer(&v.ptr)
    }

Issue: flagAddr and flagIndir have different semantics:

  • flagAddr indicates the value is addressable
  • flagIndir indicates the value is stored indirectly (ptr points to value, not contains it)

Question: Is this change intentional? A value can be addressable without being indirect, and this change could affect how arguments are passed to FFI calls. This needs careful verification that it doesn't break cases where values are addressable but stored directly.


2. Potential Alignment Issue in resolveIndirectValue Default Case (value.go:2427-2431)

Location: runtime/internal/lib/reflect/value.go:2427-2431

default:
    // convert abi.Bool~abi.Uintptr and abi.Struct/abi.Array themselves.
    // struct/array elem conversion by caller func.
    if typ.Size_ < unsafe.Sizeof(0) {
        v.ptr = truncate(*(*unsafe.Pointer)(v.ptr), typ.Size_*8)
    } else {
        v.ptr = *(*unsafe.Pointer)(v.ptr)
    }

Issues:

a) Struct layout correctness: For small structs/arrays (< word size), loading *(*unsafe.Pointer)(v.ptr) reads a full word. The truncate() function masks by bit count, but this assumes:

  • The value is stored in the low-order bits contiguously
  • There's no padding or field alignment within the struct

This may be incorrect for:

  • Structs with explicit padding
  • Packed structs with unusual layouts
  • Arrays with non-byte-aligned elements

b) Comment inaccuracy: The comment says "convert abi.Bool~abi.Uintptr and abi.Struct/abi.Array" but the default case also handles other types that don't match the earlier cases (Float32/Float64). The comment should clarify what "themselves" means.

Recommendation:

  • Add explicit handling for Struct/Array types that respects field boundaries
  • Or document why the current approach is safe for all struct/array layouts
  • Clarify the comment about which types are handled here

3. Unconditional Pointer Conversion of Receiver Type (value.go:2778-2781)

Location: runtime/internal/lib/reflect/value.go:2778-2781

if rcvrtype.Kind() != abi.Pointer {
    // convert rcvrtype to abi.Pointer to match ifn call
    rcvrtype = toRType(rcvrtype).ptrTo()
}

Issue: This unconditionally converts all non-pointer receiver types to pointers. While this may be correct for the FFI calling convention, it raises questions:

a) Value receiver semantics: For methods with explicit value receivers, does converting the type to pointer affect:

  • Type identity checks?
  • Reflection type queries?
  • Method signature matching?

b) Missing documentation: The comment "to match ifn call" is vague. It should explain:

  • Why FFI requires pointer types for all receivers
  • Whether this affects both Ifn_ and Tfn_ equally
  • If there are any cases where this conversion is wrong

Recommendation: Add comprehensive documentation explaining the calling convention requirement and verify this doesn't break type identity checks elsewhere.


Medium Priority Issues

4. storeRcvr Condition Ordering (value.go:2448-2458)

Location: runtime/internal/lib/reflect/value.go:2448-2458

if t.Kind() == abi.Interface {
    *(*unsafe.Pointer)(p) = ifacePtrData(iface)
} else if v.flag&flagIndir != 0 && !ifaceIndir(t) {
    *(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
} else if v.flag&flagIndir == 0 && runtime.DirectIfaceData(t) {
    *(*unsafe.Pointer)(p) = unsafe.Pointer(&v.ptr)
} else {
    *(*unsafe.Pointer)(p) = v.ptr
}

Issue: The condition ordering could be clearer. The third condition (flagIndir == 0 && DirectIfaceData) handles direct-stored small values that need their address taken.

Questions:

  • Why does the flagIndir != 0 check come before the flagIndir == 0 check?
  • Could these conditions be reordered for better logical flow?
  • Are there any cases where a value matches multiple conditions?

Recommendation: Add comments documenting what each branch handles and why the ordering matters, or reorder for clarity.


5. IsClosure Check Addition (value.go:1128, 1735)

Location: runtime/internal/lib/reflect/value.go:1128 and 1735

// Old code
if p != nil {
    p = *(*unsafe.Pointer)(p)
}

// New code
if p != nil && v.typ_.IsClosure() {
    p = *(*unsafe.Pointer)(p)
}

Good: This correctly distinguishes between closure types and direct function pointers.

Question: Are there any edge cases where:

  • A function pointer is nil but IsClosure() is true?
  • A non-closure function type incorrectly has its pointer dereferenced?

Recommendation: Verify that IsClosure() accurately reflects whether the function value is stored as a closure structure in all cases (regular functions, method values, MakeFunc results, etc.).


Minor Issues

6. FuncOf Closure Type Extraction (type.go:1242-1244)

Location: runtime/internal/lib/reflect/type.go:1242-1244

// Closures are always struct{fn *funcType, data unsafe.Pointer}
// This is guaranteed by closureOf() construction
closureType := *(**abi.StructType)(unsafe.Pointer(&ifunc))
prototype := closureType.Fields[0].Typ.FuncType()

Good: The added comment explains the closure structure invariant.

Minor concern: This assumes closureType.Fields[0] always exists and is a function type. While this should be guaranteed by closureOf(), consider adding an assertion or panic for defense:

if len(closureType.Fields) < 1 {
    panic("reflect: invalid closure type structure")
}

7. makeMethodValue Simplification (makefunc.go:218-224)

Location: runtime/internal/lib/reflect/makefunc.go:218-224

_, _, fn := methodReceiver(op, rcvr, int(v.flag)>>flagMethodShift)
var ptr unsafe.Pointer
storeRcvr(v, unsafe.Pointer(&ptr))
fv := &struct {
    fn  unsafe.Pointer
    env unsafe.Pointer
}{fn, ptr}

Good: This correctly uses storeRcvr with the original v to preserve flags.

Observation: The ordering matters - methodReceiver is called with rcvr (method bit cleared), but storeRcvr is called with original v. This is correct but subtle. Consider adding a comment explaining why both values are needed.


SSA Changes

8. needAbiInit Detection Movement (ssa/decl.go, ssa/expr.go)

Location: Moved from ssa/decl.go:NewFuncEx to ssa/expr.go:Call

Good: Moving detection to the call site is more accurate, as it detects actual usage rather than just function declaration.

Question: The new location adds reflect.Value.Method to the list of functions requiring ABI init. Is this exhaustive? Are there other reflect methods that should trigger ABI init?

Recommendation: Document why these specific reflect functions require ABI initialization.


Testing Concerns

9. Demo Test Coverage (_demo/go/reflectmethod/main.go)

Good: The 1145-line demo provides comprehensive test coverage for:

  • Value and pointer receiver methods
  • Direct interface methods
  • Small types (byte), word-sized types (uintptr), large types (arrays)
  • Variadic methods
  • Float return values
  • Multi-return values

Missing: Consider adding tests for:

  • Methods on interface values with small direct-stored types (int8, bool)
  • Methods returning structs with mixed field types
  • Edge cases around alignment boundaries
  • Nil function values with IsClosure checks

Summary

Critical concerns requiring attention:

  1. flagAddr → flagIndir change - Verify this doesn't break addressable-but-direct values
  2. resolveIndirectValue default case - Verify correctness for complex struct/array layouts
  3. Unconditional rcvrtype pointer conversion - Document why this is always correct

Positive aspects:

  • IsClosure() checks properly distinguish closure from function pointers
  • FuncOf properly extracts function type from closure structure
  • storeRcvr handles direct interface data correctly
  • Comprehensive test coverage in demo

Recommendations before merge:

  1. Verify the flagAddrflagIndir change doesn't introduce regressions
  2. Add documentation explaining receiver pointer conversion requirement
  3. Clarify or improve struct/array handling in resolveIndirectValue
  4. Add comments for non-obvious condition ordering in storeRcvr

👉 Want me to re-run this review? Just type /review in the comments! For more usage examples, visit CodeAgent GitHub Repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant