Skip to content

ssa: panic with runtime type assertion errors#1892

Closed
cpunion wants to merge 56 commits into
xgo-dev:mainfrom
cpunion:codex/goroot-interface-coverage
Closed

ssa: panic with runtime type assertion errors#1892
cpunion wants to merge 56 commits into
xgo-dev:mainfrom
cpunion:codex/goroot-interface-coverage

Conversation

@cpunion

@cpunion cpunion commented May 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • lower failed non-comma-ok type assertions through a runtime TypeAssertionError helper instead of panic(string)
  • add stable test/go coverage for recovered runtime.Error values on interface-to-interface and interface-to-concrete assertions
  • remove fixed fixedbugs/issue16130.go GOROOT xfails; leave issue32187 comparison xfails untouched

Testing

  • go test ./test/go -run 'TestInterfaceAssert|TestNilInterfaceSameTypeAssertPanics|TestInterfaceCompareUncomparable' -count=1\n- go test ./test/go -count=1\n- (cd runtime && go test ./internal/runtime -run 'TestDoesNotExist' -count=1)\n- LLGO_ROOT="$PWD" go run -tags=dev ./cmd/llgo test -run 'TestInterfaceAssert|TestNilInterfaceSameTypeAssertPanics|TestInterfaceCompareUncomparable' ./test/go\n- go test ./ssa -count=1\n- go test ./test/goroot -run TestGoRootRunCases/fixedbugs/issue16130.go -count=1 -args -goroot "$(go env GOROOT)" -dirs fixedbugs -case '^fixedbugs/issue16130\.go$' -xfail /tmp/llgo-no-xfail.yaml\n- go test ./test/goroot -run TestGoRootRunCases/fixedbugs/issue16130.go -count=1 -args -goroot "$(go env GOROOT)" -dirs fixedbugs -case '^fixedbugs/issue16130\.go$'\n\n## Notes\nGOROOT CI is slow/disabled, so this PR does not depend on automatic full GOROOT runs. The targeted local GOROOT check used the current machine GOROOT (go env GOROOT = /opt/homebrew/Cellar/go@1.24/1.24.11/libexec). Normal PR CI should still run from the fork branch.\n\nA non-targeted local LLGO_ROOT="$PWD" go run -tags=dev ./cmd/llgo test ./test/go run reached test execution but failed on darwin temp-dir cleanup with operation not permitted in existing temp-dir cleanup paths; the focused interface assertion llgo test above passed.

@codecov-commenter

codecov-commenter commented May 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@cpunion

cpunion commented May 24, 2026

Copy link
Copy Markdown
Collaborator Author

Update for latest interface/nointerface coverage (head c9e354c):

Scope:

  • interface/nointerface only; no chan/nil/print/recover/GC/liveness/finalizer/goroutine changes.
  • //go:nointerface methods are recorded during AST directive scan and omitted from runtime ABI method tables, while direct method calls/method expressions remain available.

GOROOT xfails removed after targeted local verification:

  • typeparam/mdempsky/15.go (//go:nointerface, generics, promoted methods)
  • fixedbugs/issue47928.go (promoted //go:nointerface method)
  • typeparam/mdempsky/16.go (interface type assertion panic text; already fixed by this PR's earlier assertion-error work)

Coverage:

  • Added test/go/nointerface_test.go plus llgo/non-llgo expectation files. Host Go's default toolchain does not filter nointerface without the fieldtrack experiment, while llgo should filter under the llgo tag.

Local tests:

  • go test ./test/go -count=1
  • go test ./ssa -count=1
  • go test ./cl -count=1
  • go test ./test/goroot -run TestGoRootRunCases -count=1 -args -goroot "$(go env GOROOT)" -dirs typeparam/mdempsky,fixedbugs -case '^(typeparam/mdempsky/(15|16)|fixedbugs/issue47928)\.go$' -directive-mode runlike -run-timeout 120s

Not covered here:

  • Remaining non-nointerface xfails such as typeparam/nested.go and unrelated chan/nil/print/recover/GC cases are intentionally left for their owning domains.

@cpunion

cpunion commented May 24, 2026

Copy link
Copy Markdown
Collaborator Author

Update for commit 64ae33c:\n\n- Added reflect/interface type identity coverage for reflect.Type.Method and MethodByName: Method.Func.Interface() now asserts to and calls func(*T) / func(T) using the canonical LLGo closure-backed function type.\n- Fixed reflect.Type interface invokes to request method ABI type initialization, included closure ABI symbols for reflect method FuncOf lookup, and boxed Type.Method Func values as LLGo closure values while preserving public reflect Type identity.\n- recover.go classification: without #1882 it still stops at the existing recover-frame failure (spurious recover). With #1882's recover-frame fix layered locally, the previous func(*T1) vs func(*T1) type identity failure is gone; the next observed failure is missing recover 10, which is recover semantics and intentionally not covered here. No recover.go xfail removed.\n- Local tests: go test ./test/go -count=1; go run ./cmd/llgo test -run TestReflectTypeMethodFuncInterfaceTypeIdentity -count=1 ./test/go; go test ./ssa -count=1; go test ./cl -count=1; go test ./internal/build -count=1; focused goroot recover.go with existing xfail passed.

@cpunion

cpunion commented May 24, 2026

Copy link
Copy Markdown
Collaborator Author

Coverage update for fixedbugs/issue26094.go (commit 5733be6aa6aa6f2bb3782a00ae49e7bc8c960989):

  • Classified as the same local type identity / interface assertion root as this draft PR: different scopes can produce the same printed type name, but interface assertion must compare scoped type identity and panic with different scopes.
  • Added stable test/go coverage in TestInterfaceAssertRejectsSameNameTypesFromDifferentScopes for local-vs-local and local-vs-package same-name type assertions.
  • Removed only the now-covered fixedbugs/issue26094.go goroot xfail entries for darwin/arm64 and linux/amd64. No nil/chan/print/recover/GC/finalizer/goroutine cases were changed.

Focused local validation:

  • go test ./test/go -run 'TestInterfaceAssert(RejectsSameNameTypesFromDifferentScopes|To.*Panics)' -count=1\n- go test ./test/goroot -run TestGoRootRunCases/fixedbugs/issue26094.go -count=1 -args -goroot /Users/lijie/sdk/go1.26.0 -dirs fixedbugs -case '^fixedbugs/issue26094\\.go$' -progress 30s\n- go test ./test/go -count=1\n- go test ./ssa/... ./cl/... ./runtime/... -count=1 reported ssa and cl OK; the command itself returned FAIL because ./runtime/... is a separate module path from the repo root.\n- (cd runtime && go test ./... -count=1) passed.

@cpunion

cpunion commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator Author

Merged latest xgo-dev/main and resolved the remaining #1892 conflicts.

Conflict/coverage updates:

  • Kept the mainline reflect Method.Func closure boxing and combined it with this PR's interface assertion closure matching semantics.
  • Fixed the malformed adjacent xfail entries for typeparam/issue51521.go and typeparam/orderedmap.go while keeping ssa: panic with runtime type assertion errors #1892-owned xfails removed.
  • Added package-level coverage for //go:nointerface parsing/filtering and reflect-method closure ABI symbol filtering: cl/import_coverage_test.go, ssa/nointerface_test.go, internal/build/main_module_test.go.

Local verification:

  • go test -timeout 20m ./test/go -run 'Test(InterfaceAssert|NilInterfaceSameTypeAssertPanics|InterfaceCompareUncomparable|NoInterface|ReflectTypeMethodFuncInterfaceTypeIdentity)' -count=1\n- go test -timeout 20m ./ssa ./cl -run 'Test.*(Interface|NoInterface|FuncType|Method|TypeAssert|Import|ABI|Closure)|TestDoesNotExist' -count=1\n- cd runtime && go test -timeout 20m ./internal/runtime ./internal/lib/reflect -count=1\n- go test -timeout 30m ./test/goroot -run TestGoRootRunCases -count=1 -args -goroot /Users/lijie/sdk/go1.26.0 -dirs fixedbugs,typeparam/mdempsky -case '^(fixedbugs/(issue16130|issue26094|issue47928)|typeparam/mdempsky/(15|16))\.go$' -directive-mode runlike -run-timeout 120s\n- go test -timeout 20m ./cl -coverprofile=/tmp/llgo-1892-cl.cover -count=1 (93.8%)\n- go test -timeout 20m ./ssa -coverprofile=/tmp/llgo-1892-ssa.cover -count=1 (87.9%)\n- go test -timeout 20m ./internal/build -coverprofile=/tmp/llgo-1892-build.cover -count=1 (67.4%)\n- LLGO_ROOT="$PWD" go run -tags=dev ./cmd/llgo test -run 'Test(InterfaceAssert|NilInterfaceSameTypeAssertPanics|NoInterface|ReflectTypeMethodFuncInterfaceTypeIdentity)' -count=1 ./test/go\n- git diff --check && git diff --cached --check

@cpunion
cpunion force-pushed the codex/goroot-interface-coverage branch 5 times, most recently from 14ffd15 to f0c4fd2 Compare June 10, 2026 11:30
@cpunion
cpunion force-pushed the codex/goroot-interface-coverage branch from 71068c1 to 2cf0202 Compare June 20, 2026 02:54
@cpunion
cpunion force-pushed the codex/goroot-interface-coverage branch 2 times, most recently from aafb17c to 8412508 Compare June 27, 2026 14:54
cpunion and others added 18 commits July 2, 2026 22:01
…table

On Mach-O, pointer slots that name exported functions — every
__llgo_stub.* wrapper and any exported Go function — are emitted as
chained-fixup BIND nodes, not rebases. The rewriter only decoded rebase
nodes, so all stub records (and some entry records) were dropped as
unowned and never reached the prebuilt ftab; FuncForPC on function
values silently fell back to dladdr (~6µs per fresh pc on darwin).

- Parse the LC_DYLD_CHAINED_FIXUPS imports table and resolve bind
  ordinals to their in-image definitions.
- Match canonical owners against the record symbolID with underscore
  normalization (debug/macho's suffix-shared string table can surface
  one mangling underscore more or less than the source-level name).
- Splice the prebuilt header's base slot back into the fixup chain as a
  live rebase node: dyld writes the slid text base at load, so the
  runtime reads a ready runtime PC with no slide arithmetic (non-PIE
  ELF link-time values already equal runtime addresses).
- LLGO_PCLNPOST=0 escape hatch keeps first-use construction.

Fresh-pc FuncForPC slow path: darwin 6-8µs -> 1.2-1.7µs, linux
6.8µs -> 0.5µs; first-in-process lookup: darwin ~32µs -> ~14µs,
linux ~6.8µs -> ~4µs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pure-compute probes (recursive fib, JSON round-trip, sort.Ints, map
churn) with no runtime introspection, so one harness run covers both
the introspection extremes and what the funcinfo machinery costs code
that never asks for it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Go's pclntab pages are touched by its own runtime (traceback, GC) long
before user code queries it, so its first FuncForPC never pays page-in.
Mirror that: when the prebuilt table is present, init adopts it
(zero-copy, sub-µs), touches the pages the lookup path reads (blob,
funcinfo records, string offsets, strings), runs one synthetic lookup
to warm the code paths, and write-warms the FuncForPC cache pages.

First-in-process FuncForPC: darwin ~17µs -> ~2.8µs, linux ~6.6µs ->
~1.0µs. Startup cost is page-count-bound (tens of µs on stdlib-sized
tables, invisible next to ~3ms process startup; hello-world medians
unchanged). Non-prebuilt binaries stay fully lazy: first-use
construction allocates, which has no place in init, and programs that
never introspect pay nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-depths generates deep_<N> scenarios at configurable call depths;
-bigsizes generates bigfunc scenarios (funcs x statements) whose large
bodies stress statement-level pcline density, mid-function pc
symbolization, and ordinary performance of big method bodies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cpunion
cpunion force-pushed the codex/goroot-interface-coverage branch from 8412508 to aacb3cc Compare July 2, 2026 15:03
@cpunion

cpunion commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto #2016 (codex/pclntab-linkphase-p1, which includes #2012) per the review-order plan: #2012#2016 → this line of semantics fixes. Conflicts resolved were additive (context fields, the noinline condition set, and runtime.Panic now calls SavePanicCallerFrames() before the panic-node bookkeeping). Note the PR base is still main, so the diff shows #2012/#2016 commits until those merge.

@cpunion

cpunion commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #2031, which re-expresses this PR's surviving value (go:nointerface support, scope-disambiguated assertion messages, MatchesClosure and reflect.Method fixes) on the stage-5 base; the funcinfo/pclntab machinery carried here was superseded by #2012/#2016/#2019.

@cpunion cpunion closed this Jul 6, 2026
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 8, 2026
…upport

Re-expresses the surviving value of xgo-dev#1892 on the xgo-dev#2023 base (its other
~10k lines were an earlier draft of the funcinfo/pclntab machinery,
since superseded by xgo-dev#2012/xgo-dev#2016/xgo-dev#2019):

- //go:nointerface methods no longer satisfy interfaces: the pragma is
  recorded at import (cl/import.go -> Program.SetNoInterfaceMethod) and
  filtered out of abi uncommon method tables; GlobalDCE metadata takes
  the filtered selection list. Retires the typeparam/mdempsky/15
  goroot xfails (validated go1.26 darwin, ci directive mode).
- Type-assertion failure between same-named types from different
  scopes appends '(types from different scopes)' (gc wording).
- MatchesClosure unwraps closure types on both sides, fixing assertions
  between closure and plain function types.
- reflect.(*rtype).Method builds the method func value with the closure
  layout allocation (fixes reflect method-value identity).
- xfail: retire fixedbugs/issue16130, fixedbugs/issue29735 (validated
  go1.24 + go1.26 darwin), and reword typeparam/mdempsky/16 - the
  panic value now implements runtime.Error; the message still lacks
  the source interface type and prints the command-line-arguments
  package path.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 8, 2026
…upport

Re-expresses the surviving value of xgo-dev#1892 on the xgo-dev#2023 base (its other
~10k lines were an earlier draft of the funcinfo/pclntab machinery,
since superseded by xgo-dev#2012/xgo-dev#2016/xgo-dev#2019):

- //go:nointerface methods no longer satisfy interfaces: the pragma is
  recorded at import (cl/import.go -> Program.SetNoInterfaceMethod) and
  filtered out of abi uncommon method tables; GlobalDCE metadata takes
  the filtered selection list. Retires the typeparam/mdempsky/15
  goroot xfails (validated go1.26 darwin, ci directive mode).
- Type-assertion failure between same-named types from different
  scopes appends '(types from different scopes)' (gc wording).
- MatchesClosure unwraps closure types on both sides, fixing assertions
  between closure and plain function types.
- reflect.(*rtype).Method builds the method func value with the closure
  layout allocation (fixes reflect method-value identity).
- xfail: retire fixedbugs/issue16130, fixedbugs/issue29735 (validated
  go1.24 + go1.26 darwin), and reword typeparam/mdempsky/16 - the
  panic value now implements runtime.Error; the message still lacks
  the source interface type and prints the command-line-arguments
  package path.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 8, 2026
…upport

Re-expresses the surviving value of xgo-dev#1892 on the xgo-dev#2023 base (its other
~10k lines were an earlier draft of the funcinfo/pclntab machinery,
since superseded by xgo-dev#2012/xgo-dev#2016/xgo-dev#2019):

- //go:nointerface methods no longer satisfy interfaces: the pragma is
  recorded at import (cl/import.go -> Program.SetNoInterfaceMethod) and
  filtered out of abi uncommon method tables; GlobalDCE metadata takes
  the filtered selection list. Retires the typeparam/mdempsky/15
  goroot xfails (validated go1.26 darwin, ci directive mode).
- Type-assertion failure between same-named types from different
  scopes appends '(types from different scopes)' (gc wording).
- MatchesClosure unwraps closure types on both sides, fixing assertions
  between closure and plain function types.
- reflect.(*rtype).Method builds the method func value with the closure
  layout allocation (fixes reflect method-value identity).
- xfail: retire fixedbugs/issue16130, fixedbugs/issue29735 (validated
  go1.24 + go1.26 darwin), and reword typeparam/mdempsky/16 - the
  panic value now implements runtime.Error; the message still lacks
  the source interface type and prints the command-line-arguments
  package path.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 9, 2026
…upport

Re-expresses the surviving value of xgo-dev#1892 on the xgo-dev#2023 base (its other
~10k lines were an earlier draft of the funcinfo/pclntab machinery,
since superseded by xgo-dev#2012/xgo-dev#2016/xgo-dev#2019):

- //go:nointerface methods no longer satisfy interfaces: the pragma is
  recorded at import (cl/import.go -> Program.SetNoInterfaceMethod) and
  filtered out of abi uncommon method tables; GlobalDCE metadata takes
  the filtered selection list. Retires the typeparam/mdempsky/15
  goroot xfails (validated go1.26 darwin, ci directive mode).
- Type-assertion failure between same-named types from different
  scopes appends '(types from different scopes)' (gc wording).
- MatchesClosure unwraps closure types on both sides, fixing assertions
  between closure and plain function types.
- reflect.(*rtype).Method builds the method func value with the closure
  layout allocation (fixes reflect method-value identity).
- xfail: retire fixedbugs/issue16130, fixedbugs/issue29735 (validated
  go1.24 + go1.26 darwin), and reword typeparam/mdempsky/16 - the
  panic value now implements runtime.Error; the message still lacks
  the source interface type and prints the command-line-arguments
  package path.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 17, 2026
…upport

Re-expresses the surviving value of xgo-dev#1892 on the xgo-dev#2023 base (its other
~10k lines were an earlier draft of the funcinfo/pclntab machinery,
since superseded by xgo-dev#2012/xgo-dev#2016/xgo-dev#2019):

- //go:nointerface methods no longer satisfy interfaces: the pragma is
  recorded at import (cl/import.go -> Program.SetNoInterfaceMethod) and
  filtered out of abi uncommon method tables; GlobalDCE metadata takes
  the filtered selection list. Retires the typeparam/mdempsky/15
  goroot xfails (validated go1.26 darwin, ci directive mode).
- Type-assertion failure between same-named types from different
  scopes appends '(types from different scopes)' (gc wording).
- MatchesClosure unwraps closure types on both sides, fixing assertions
  between closure and plain function types.
- reflect.(*rtype).Method builds the method func value with the closure
  layout allocation (fixes reflect method-value identity).
- xfail: retire fixedbugs/issue16130, fixedbugs/issue29735 (validated
  go1.24 + go1.26 darwin), and reword typeparam/mdempsky/16 - the
  panic value now implements runtime.Error; the message still lacks
  the source interface type and prints the command-line-arguments
  package path.
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.

2 participants