Skip to content

Don't dereference a null type when building a template argument name#732

Merged
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:fix-template-argument-null-type
Jul 13, 2026
Merged

Don't dereference a null type when building a template argument name#732
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:fix-template-argument-null-type

Conversation

@tannergooding

Copy link
Copy Markdown
Member

Fixes #580.

Generation crashes with a NullReferenceException while building the qualified name of a class template specialization:

System.NullReferenceException: Object reference not set to an instance of an object.
   at ClangSharp.PInvokeGenerator.<GetCursorQualifiedName>g__AppendTemplateArgument|...
   at ClangSharp.PInvokeGenerator.<GetCursorQualifiedName>g__AppendTemplateArguments|...
   at ClangSharp.PInvokeGenerator.GetCursorQualifiedName(NamedDecl, Boolean)
   at ClangSharp.PInvokeGenerator.<VisitTypedefDecl>g__ForUnderlyingType|...

Root cause. AppendTemplateArgument handles a Type-kind argument with templateArgument.AsType.AsString. AsType routes through TranslationUnit.GetOrCreate<Type>, which returns null for a CXType_Invalid handle (the Debug.Assert guarding that path is compiled out of Release). So when an argument's underlying QualType maps to an invalid CXType — an unsupported/exotic type the shim can't represent, as happens deep in the sol2 / spdlog / C++20 template instantiations from the report — AsType is null and .AsString throws. AsIntegral returns a long and the default branch is already safe, so this is the only unguarded deref.

Fix. Guard the access and fall back to the same '?' placeholder the default branch already uses for unsupported argument kinds:

var argType = templateArgument.AsType;
_ = qualifiedName.Append((argType is not null) ? argType.AsString : ""?"");

The success path is byte-identical (non-null AsType still yields AsType.AsString); the only new behavior is that a previously-fatal NRE now renders as Foo<?> in the qualified name (used for diagnostics and remapping keys), matching how unsupported kinds already render.

On testing. I verified the mechanism directly from source and confirmed it's the only null-deref in the method. I could not build a minimal standalone repro — the invalid-CXType argument only arises from the specific deeply-nested standard-library instantiations in the original report, which need the full ModEngine2 + sol2 + spdlog include tree and toolchain to stand up. This mirrors the earlier harden generator against malformed inputs change, which likewise guards a native-only crash path that isn't expressible through the golden-file harness. Full suite green with zero golden changes.

Note

This PR description and the code change were drafted by Copilot on my behalf.

GetCursorQualifiedName builds a qualified name for a class template
specialization by appending each template argument. For a Type-kind argument
it did templateArgument.AsType.AsString, but AsType is null when the
argument's underlying type maps to an invalid CXType (an unsupported type the
shim can't represent), so GetOrCreate<Type> returns null and the deref throws
a NullReferenceException during generation.

Guard the access and fall back to the same '?' placeholder already used for
other unsupported argument kinds. The success path is byte-identical.

Fixes dotnet#580

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@tannergooding tannergooding merged commit dfe9fe3 into dotnet:main Jul 13, 2026
14 checks passed
@tannergooding tannergooding deleted the fix-template-argument-null-type branch July 13, 2026 05:58
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.

Null Reference Exception during generation in PInvokeGenerator.cs#L2960

1 participant