Skip to content

Fix nested generic TypeSpec emission#235

Merged
josesimoes merged 2 commits intonanoframework:developfrom
Eclo:fix-typespec-signature
May 6, 2026
Merged

Fix nested generic TypeSpec emission#235
josesimoes merged 2 commits intonanoframework:developfrom
Eclo:fix-typespec-signature

Conversation

@josesimoes
Copy link
Copy Markdown
Member

@josesimoes josesimoes commented Apr 28, 2026

Description

  • Emit cumulative generic-argument count and write enclosing-type arguments before nested-type arguments; compute and write absolute VAR indices for nested generic parameters.
  • Add regression test (NestedGenericTypeSpecTests) to test app exercising nested generic struct TypeSpec.
  • Improve DumpAssemblyTest() unit test to grab indexed from metadatables instead of being hardcoded.
  • Fix nested generic TypeSpec signature collision.
  • Added test class to prevent regression.

Motivation and Context

  • ECMA-335 requires cumulative generic instantiation for nested generics; previous output omitted enclosing args and used relative VAR positions, causing runtime VAR resolution failures (CLR_E_FAIL).
  • Types sharing a nested name (e.g. Enumerator) but differing only in their enclosing generic type (e.g. Container1 vs Container2<TKey,TValue>) were incorrectly mapped to the same TypeSpec entry. The root cause was that the parent type's concrete generic arguments were recovered from the uninstantiated element type's declaring type instead of from the instantiated GenericInstanceType.DeclaringType, causing signatures to be identical and MemberRefs to resolve to the wrong type at runtime.
  • The issue surfaced when developing Dictionary<TKey, TValue> in System.Collections.
  • Related with Support for generics <T> Home#782.
  • [tested against nanoclr buildId 61413].

How Has This Been Tested?

  • Added new code covering this.
  • Running System.Collections tests for Dictionary<TKey, TValue>.

Screenshots

Types of changes

  • Improvement (non-breaking change that improves a feature, code or algorithm)
  • Bug fix (non-breaking change which fixes an issue with code or algorithm)
  • New feature (non-breaking change which adds functionality to code)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Config and build (change in the configuration and build system, has no impact on code or features)
  • Dependencies (update dependencies and changes associated, has no impact on code or features)
  • Unit Tests (add new Unit Test(s) or improved existing one(s), has no impact on code or features)
  • Documentation (changes or updates in the documentation, has no impact on code or features)

Checklist:

  • My code follows the code style of this project (only if there are changes in source code).
  • My changes require an update to the documentation (there are changes that require the docs website to be updated).
  • I have updated the documentation accordingly (the changes require an update on the docs in this repo).
  • I have read the CONTRIBUTING document.
  • I have tested everything locally and all new and existing tests passed (only if there are changes in source code).
  • I have added new tests to cover my changes.

@nfbot nfbot added Type: bug Something isn't working Type: Unit Tests labels Apr 28, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

This PR updates the metadata processor to implement ECMA-335 cumulative indexing rules for nested generic types. It refactors signature encoding to compute absolute VAR indices by walking declaring-type chains and ensures generic instances include enclosing type arguments. Test assertions are made version-independent, and a new regression test validates nested generic struct TypeSpec correctness.

Changes

Cohort / File(s) Summary
Signature Encoding Logic
MetadataProcessor.Shared/Tables/nanoSignaturesTable.cs
Implements ECMA-335 cumulative indexing for nested generics: VAR parameters now compute absolute indices by traversing declaring-type chains, and generic instance types prepend missing enclosing type arguments with adjusted argument counts.
Test Infrastructure
MetadataProcessor.Tests/Core/Utility/DumperTests.cs
Converts fixed, version-sensitive AssemblyRef and TypeRef dump assertions to dynamic verification by iterating through reference tables, computing expected token formatting per entry, and asserting dumped content contains computed lines.
Regression Testing
MetadataProcessor.Tests/TestNFApp/NestedGenericTypeSpecTests.cs, MetadataProcessor.Tests/TestNFApp/Program.cs
Introduces nested generic struct regression test with Container<TOuter>.Entry<TInner> instantiations to validate TypeSpec signature preservation; integrates test execution into program flow.
Project Configuration
MetadataProcessor.Tests/TestNFApp/TestNFApp.nfproj
Adds NestedGenericTypeSpecTests.cs to project compilation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: fixing nested generic TypeSpec emission, which is the core focus of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description is directly related to the changeset, clearly explaining the bug fix for nested generic TypeSpec emission and the corresponding test additions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@josesimoes
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@MetadataProcessor.Shared/Tables/nanoSignaturesTable.cs`:
- Line 450: The cast to byte when writing cumulative indices (e.g., the
writer.WriteByte((byte)absolutePosition) call and the similar writes around
lines 548-549) can silently truncate values; before casting, validate the int
value is within 0..255 and fail fast if not (throw an informative
ArgumentOutOfRangeException or custom exception that includes the variable name
and actual value), or switch to writing a wider type if larger ranges are
expected; update the code paths that compute absolutePosition/cumulativeIndex to
perform this bounds check and include the variable name and offending value in
the thrown message.
- Around line 443-448: The code currently assumes varParam.Owner is a
TypeDefinition (declaringTypeDef) and therefore skips the MethodDefinition case;
update handling for varParam.Owner so it checks its runtime type: if Owner is a
TypeDefinition, traverse its DeclaringType chain and sum each
DeclaringType.GenericParameters.Count into absolutePosition (using the existing
declaringTypeDef loop), and if Owner is a MethodDefinition, first set a local
methodDecl = (MethodDefinition)varParam.Owner, then take
methodDecl.DeclaringType and traverse that DeclaringType chain adding each
DeclaringType.GenericParameters.Count to absolutePosition; ensure you still
account for the method-level generic parameters correctly (treat method generic
params separately as needed) and reference varParam.Owner, TypeDefinition,
MethodDefinition, declaringTypeDef, and GenericParameters.Count when making
these changes.
- Around line 526-545: The current check uses
declaringTypeRef.HasGenericParameters which is false for closed
GenericInstanceType and blocks recovering enclosing generic args; change the
logic so that you don't gate recovery on HasGenericParameters alone — first
detect if declaringTypeRef is a GenericInstanceType and recover its
GenericArguments (assign to enclosingArgs) even when HasGenericParameters is
false, and only fall back to HasGenericParameters-based logic otherwise; update
the code paths around declaringTypeRef, declaringGenericInst, enclosingArgs,
elementType and genericType.GenericArguments so enclosed-type arguments from a
GenericInstanceType are always considered.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: nanoframework/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5962d447-59d0-43b0-a74f-62ade66fa786

📥 Commits

Reviewing files that changed from the base of the PR and between 6d8cf13 and 38e7c8f.

📒 Files selected for processing (5)
  • MetadataProcessor.Shared/Tables/nanoSignaturesTable.cs
  • MetadataProcessor.Tests/Core/Utility/DumperTests.cs
  • MetadataProcessor.Tests/TestNFApp/NestedGenericTypeSpecTests.cs
  • MetadataProcessor.Tests/TestNFApp/Program.cs
  • MetadataProcessor.Tests/TestNFApp/TestNFApp.nfproj

Comment thread MetadataProcessor.Shared/Tables/nanoSignaturesTable.cs Outdated
Comment thread MetadataProcessor.Shared/Tables/nanoSignaturesTable.cs Outdated
Comment thread MetadataProcessor.Shared/Tables/nanoSignaturesTable.cs
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

An unexpected error occurred while generating fixes: Not Found - https://docs.github.com/rest/git/refs#get-a-reference

- Emit cumulative generic-argument count and write enclosing-type arguments before nested-type arguments; compute and write absolute VAR indices for nested generic parameters.
- Fix signature for nested helper types.
- Fix container index for For generic methods on non-generic declaring types.
- Add regression test (NestedGenericTypeSpecTests) to test app exercising nested generic struct TypeSpec.
- Improve DumpAssemblyTest() unit test to grab indexed from metadatables instead of being hardcoded.
- Types sharing a nested name (e.g. Enumerator) but differing only in their enclosing generic type (e.g. Container1<T> vs Container2<TKey,TValue>) were incorrectly mapped to the same TypeSpec entry. The root cause was that the parent type's concrete generic arguments were recovered from the uninstantiated element type's declaring type instead of from the instantiated GenericInstanceType.DeclaringType, causing signatures to be identical and MemberRefs to resolve to the wrong type at runtime.
- Added test class to prevent regression.
@josesimoes josesimoes force-pushed the fix-typespec-signature branch from 38e7c8f to ba15616 Compare May 6, 2026 09:30
@josesimoes josesimoes merged commit e7f5af6 into nanoframework:develop May 6, 2026
6 checks passed
@josesimoes josesimoes deleted the fix-typespec-signature branch May 6, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: bug Something isn't working Type: Unit Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants