Skip to content

reflect: fix Type.Name() returning empty string for basic types#1511

Merged
xushiwei merged 2 commits intogoplus:mainfrom
luoliwoshang:fix/issue-1412-basic-type-name
Dec 29, 2025
Merged

reflect: fix Type.Name() returning empty string for basic types#1511
xushiwei merged 2 commits intogoplus:mainfrom
luoliwoshang:fix/issue-1412-basic-type-name

Conversation

@luoliwoshang
Copy link
Member

Summary

  • Fix reflect.Type.Name() returning empty string for basic types (int, string, bool, etc.)
  • Basic types don't have TFlagNamed set in llgo, but they do have valid names stored in Str_ field
  • Add a special case in Name() to return String() for basic types (Kind <= UnsafePointer)

Test

v := 42
t := reflect.TypeOf(v)
fmt.Println(t.Name())  // Before: "", After: "int"

All basic types tested:

  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64, uintptr
  • float32, float64
  • complex64, complex128
  • string, bool

Why not just add TFlagNamed to basicFlags()?

Attempted to add TFlagNamed to basicFlags() function, but it causes segmentation fault. The TFlagNamed flag implies the type has additional metadata structures (like UncommonType), which basic types don't have. Accessing these non-existent structures causes the crash.

This fix modifies Name() method instead, which is safer and works correctly.

Fixes #1412

🤖 Generated with Claude Code

@gemini-code-assist
Copy link

Summary of Changes

Hello @luoliwoshang, 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 several key areas, including a critical fix for reflect.Type.Name() to correctly report names for basic types. It also refactors the sync.Cond primitive for better compatibility and significantly enhances the time package by adding a suite of new functionalities for time conversions, formatting, and marshaling. Furthermore, the PR introduces a robust set of development tools and scripts, leveraging Docker, to improve the local development and continuous integration workflow. These changes are supported by extensive new test cases covering binary operations, closure behavior, and the expanded time package features.

Highlights

  • Reflect Package Fix: Resolved an issue where reflect.Type.Name() returned an empty string for basic types (e.g., int, string, bool) by adding a special case to return String() for these types.
  • Sync.Cond Refactor: Updated the sync.Cond implementation to align more closely with the standard library's behavior, using gosync.Locker directly and removing pthread_cond linknames.
  • Time Package Enhancements: Expanded the time package with new methods for Unix time conversions (Unix, UnixMilli, UnixMicro), binary/JSON/text marshaling, and time manipulation (AddDate, In, Location, Zone, ZoneBounds, Truncate, Round).
  • Closure Implementation Improvements: Refined closure handling to ensure function values are correctly wrapped as closures, especially when stored in maps or used as interface methods, and added comprehensive tests for various closure scenarios.
  • New Development Tooling: Introduced new dev scripts and Docker configurations to streamline local development, including Docker-based environments, local CI checks, and simplified llgo command execution.

🧠 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.

Ignored Files
  • Ignored by pattern: .github/workflows/** (7)
    • .github/workflows/doc-link-checker.yml
    • .github/workflows/fmt.yml
    • .github/workflows/go.yml
    • .github/workflows/llgo.yml
    • .github/workflows/release-build.yml
    • .github/workflows/stdlib-coverage.yml
    • .github/workflows/test_demo.sh
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.

Basic types (int, string, bool, etc.) don't have TFlagNamed set in llgo,
but they do have valid names stored in Str_ field. This fix adds a special
case in Name() to return String() for basic types (Kind <= UnsafePointer).

Fixes goplus#1412

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@luoliwoshang luoliwoshang force-pushed the fix/issue-1412-basic-type-name branch from 5b7f657 to a362ba0 Compare December 29, 2025 03:35
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, titled to fix an issue with reflect.Type.Name(), introduces a vast range of significant improvements and refactorings across the codebase. The core fix for reflect.Type.Name() is correct and well-implemented. Beyond that, this PR includes a major refactoring of the SSA backend, particularly for closures, which greatly improves the design and correctness. It also adds a substantial implementation of the standard time package, new development tooling including Docker setup and helper scripts, and a new tool for checking standard library test coverage. While these are all valuable additions, the scope of this PR is very large, and the title is quite misleading. In the future, it would be highly beneficial to split such extensive changes into smaller, logically-grouped pull requests. This would make the review process more manageable and focused. I've found a couple of minor issues, mainly related to the new tooling and test files, which I've commented on.

I am having trouble creating individual review comments. Click here to see my feedback.

cl/_testgo/deferclosure/out.ll (1)

medium

This out.ll file, which seems to be a golden file for the deferclosure test, contains only a semicolon. The corresponding in.go file contains a main function with println calls, so it should generate non-empty LLVM IR. This looks like a mistake. Please verify if this file should be empty or if it should contain the expected LLVM IR output.

chore/check_std_symbols/main.go (231-235)

medium

The approach of parsing the output of go doc to get exported symbols is quite brittle and might break if the go doc output format changes in future Go versions. A more robust approach would be to use the go/packages library to load the package's source code and inspect its scope for exported symbols. This would provide structured access to the package's API without relying on text parsing.

For example, you could do something like this:

import (
	"go/token"
	"golang.org/x/tools/go/packages"
)

func exportedSymbols(pkgPath string) ([]symbol, error) {
	cfg := &packages.Config{Mode: packages.NeedTypes}
	pkgs, err := packages.Load(cfg, pkgPath)
	if err != nil {
		return nil, err
	}
	if len(pkgs) == 0 {
		return nil, fmt.Errorf("package not found: %s", pkgPath)
	}
	pkg := pkgs[0]
	scope := pkg.Types.Scope()
	var symbols []symbol
	for _, name := range scope.Names() {
		if token.IsExported(name) {
			obj := scope.Lookup(name)
			// ... create symbol from obj ...
		}
	}
	return symbols, nil
}

This would make the tool more resilient to changes in the Go toolchain.

@xgopilot
Copy link
Contributor

xgopilot bot commented Dec 29, 2025

Code Review Summary

This PR successfully fixes the reflect.Type.Name() issue for basic types and includes substantial infrastructure improvements. The implementation is sound, but I've identified several noteworthy areas for improvement in code quality, performance, and documentation.

Key Findings:

  • ✅ The main fix in type.go is correct and performant
  • ⚠️ Performance concern in check_std_symbols tool (repeated filepath operations)
  • 📝 Documentation could be clearer about basic type range boundary
  • 🔒 Security practices are generally good with appropriate validations

See inline comments for specific recommendations.

if !t.t.HasName() {
// For basic types (int, string, etc.), String() returns the type name directly.
// Basic types don't have TFlagNamed set, but they do have names.
if t.t.Kind() <= abi.UnsafePointer {
Copy link
Contributor

Choose a reason for hiding this comment

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

Documentation: Clarify basic type boundary

The comment mentions "basic types" but doesn't explain why <= abi.UnsafePointer is the boundary. Consider enhancing the comment to explain that this covers all predefined types (Invalid through UnsafePointer = kinds 0-25), while composite types (Array, Chan, Func, etc.) correctly return empty string when unnamed.

Suggested addition:

// For basic types (int, string, etc.), String() returns the type name directly.
// Basic types don't have TFlagNamed set, but they do have names.
// Check if Kind <= abi.UnsafePointer (25), which covers all basic predefined types
// from Invalid(0) through UnsafePointer(25). Types beyond this are composite types.

@xgopilot
Copy link
Contributor

xgopilot bot commented Dec 29, 2025

Additional Feedback

ssa/closure_wrap.go

Documentation & Safety (line 90):
The critical information about intentionally skipping null checks should be elevated to the function-level documentation. Consider adding:

// closureWrapPtr wraps a raw function pointer by loading it from ctx.
// The ctx parameter is treated as a pointer to a stored function pointer cell.
// 
// SAFETY: This function does not perform null checks on ctx. The caller must
// ensure ctx is a valid non-nil pointer. Invalid ctx values indicate a compiler
// or user error and will result in undefined behavior.

chore/check_std_symbols/main.go

Performance Optimization (lines 586-599 - pathWithinAbs):
This function is called for every identifier/selector in the AST (potentially thousands of times), performing expensive filepath.Abs() and filepath.Rel() operations repeatedly on the same paths.

Recommendation: Cache absolute path results per unique filename. This could provide 10-100x improvement for large codebases:

// In usedSymbols(), create a cache
absPathCache := make(map[string]string)
getAbsPath := func(path string) (string, bool) {
    if abs, ok := absPathCache[path]; ok {
        return abs, true
    }
    abs, err := filepath.Abs(path)
    if err != nil {
        return "", false
    }
    absPathCache[path] = abs
    return abs, true
}

Performance - Check Ordering (lines 365, 371):
In isIdentifierInDir() and isSelectorInDir(), the expensive pathWithinAbs() check happens before the cheap strings.HasSuffix() check. Reordering would enable faster short-circuiting:

if !strings.HasSuffix(pos.Filename, "_test.go") {  // Fast rejection
    return false
}
return pathWithinAbs(dirAbs, pos.Filename)  // Only for test files

Code Quality - Map Allocation (line 217):
The intrinsics map in shouldSkipSymbol() is allocated on every call. Move to package-level constant:

var unsafeIntrinsics = map[string]bool{
    "Alignof": true, "ArbitraryType": true, "IntegerType": true,
    "Offsetof": true, "Sizeof": true, "String": true, "StringData": true,
}

Security - Defensive Validation (line 602):
Add validation inside runGoDoc() for defense-in-depth:

func runGoDoc(pkgPath string) ([]byte, error) {
    if err := validatePkgPath(pkgPath); err != nil {
        return nil, err
    }
    cmd := exec.Command("go", "doc", "-all", pkgPath)
    // ... rest
}

@codecov
Copy link

codecov bot commented Dec 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.93%. Comparing base (e71a63f) to head (f84928d).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1511   +/-   ##
=======================================
  Coverage   90.93%   90.93%           
=======================================
  Files          44       44           
  Lines       11528    11528           
=======================================
  Hits        10483    10483           
  Misses        883      883           
  Partials      162      162           

☔ 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.

Add demo program to verify reflect.Type.Name() returns correct name
for basic types (int, string, etc.) instead of empty string.

Relates to goplus#1412

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@xushiwei xushiwei merged commit 52a5c15 into goplus:main Dec 29, 2025
43 checks passed
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.

reflect.TypeOf got empty reflect.Type.Name()

2 participants