Skip to content

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Oct 14, 2025

  • Understand the issue: null-conditional operator with pointer return types
  • Add test cases to verify current behavior
  • Identified that pointers are currently rejected in value context
  • Determined the fix: allow pointers in null-conditional expressions
  • Implement the fix in Binder_Expressions.cs
  • Update and add comprehensive test cases
  • Run tests to verify the fix works correctly
  • Clean up temporary test files
  • Run broader test suite to ensure no regressions (all 205+ tests passing)
  • Code review completed and feedback addressed
  • Added WorkItem attributes to all test methods

Summary

This PR adds support for the null-conditional operator (?.) when the return value is a pointer type, fixing the issue where code like byte* ptr = x?.Ptr; was incorrectly rejected with error CS8978.

Changes Made

1. Modified Binder_Expressions.cs:

  • Changed check from IsPointerOrFunctionPointer() to IsFunctionPointer() to allow regular pointers while still rejecting function pointers
  • Updated comments to clarify that regular pointers are allowed because they can represent null as the zero value
  • Added condition to skip Nullable<T> wrapping for pointer types since pointers can already represent null

2. Added 7 comprehensive tests in NullConditionalAssignmentTests.cs:

  • PointerReturnType_Simple: Basic functionality test
  • PointerReturnType_WithUsage: Runtime behavior verification
  • PointerReturnType_IntPointer: Tests with int*
  • PointerReturnType_VoidPointer: Tests with void*
  • PointerReturnType_Chained: Chained access scenarios
  • PointerReturnType_StatementContext: Statement context usage
  • FunctionPointerReturnType_StillRejected: Verifies function pointers are still rejected
  • All tests now have [WorkItem("https://github.com/dotnet/roslyn/issues/7502")] attributes

Technical Details

  • Pointers can represent null as the zero value (0), so no Nullable<T> wrapping needed
  • When the receiver is null, the expression returns a null pointer (0)
  • Function pointers continue to be rejected with CS8978
  • All existing tests pass (205+ conditional access tests)
  • Explicit = null initialization in tests avoids CS0649 warnings for cleaner test output
Original prompt

This section details on the original issue you should resolve

<issue_title>Support null-conditional operator when the return value is a pointer type</issue_title>
<issue_description>``` csharp
public class A
{
public byte* Ptr;
}


The following code will not compile.

``` csharp
var x = new A ();
byte* ptr = x?.Ptr;

The error itself doesnt make a lot of sense, so my guess it could be a potential bug:

Error CS0023 -- Operator '?' cannot be applied to operand of type 'byte*'

If not it is a quite handy operator to deal with pointers.
</issue_description>

Comments on the Issue (you are @copilot in this section)

@VSadov If this worked, it would need to return "byte_?" or Nullable. I vaguely remember that pointers cannot be used as generic type args, so it is a CLR limitation. UIntPtr would have worked though. The error is indeed somewhat confusing. It should not complain about operand, the problem is with result type here.

Fixes #7502


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot AI and others added 2 commits October 14, 2025 10:20
- Modified Binder_Expressions.cs to allow pointer types in null-conditional expressions
- Changed check from IsPointerOrFunctionPointer() to IsFunctionPointer() to allow regular pointers
- Added condition to skip Nullable<T> wrapping for pointer types
- Pointers can represent null as the zero value, so no need for Nullable<T>
- Function pointers are still correctly rejected
- Added comprehensive tests covering various pointer scenarios
- All existing tests continue to pass

Co-authored-by: CyrusNajmabadi <[email protected]>
@Copilot Copilot AI changed the title [WIP] Support null-conditional operator for pointer types Support null-conditional operator when the return value is a pointer type Oct 14, 2025
@Copilot Copilot AI requested a review from CyrusNajmabadi October 14, 2025 10:42
Copilot finished work on behalf of CyrusNajmabadi October 14, 2025 10:42
}

// The resulting type must be either a reference type T or Nullable<T>
// The resulting type must be either a reference type T, Nullable<T>, or a pointer type.
Copy link
Member

Choose a reason for hiding this comment

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

@dotnet/roslyn-compiler i think this is correct. The spec says this: https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/expressions.md#1177-null-conditional-member-access

A null_conditional_member_access expression E is of the form P?.A. Let T be the type of the expression P.A. The meaning of E is determined as follows:

Otherwise the type of E is T, and the meaning of E is the same as the meaning of:

- ((object)P == null) ? null : P.A
- Except that P is evaluated only once.

So my reading from teh spec is that this should be legal.

Copy link
Member

Choose a reason for hiding this comment

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

https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1288-null-conditional-member-access has revised the wording somewhat but I think Cyrus's interpretation still holds.

Copy link
Member

Choose a reason for hiding this comment

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

The latest docs show this:

Let T be the type of the expression P.A.

...

Otherwise the type of E is T, and the meaning of E is the same as the meaning of:

((object)P == null) ? (T)null : P.A
Except that P is evaluated only once.

graph, symbol);
}

[Fact]
Copy link
Member

Choose a reason for hiding this comment

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

@copilot these should have WorkItem attributes on them. The WorkItem should only have one argument. The url to the issue this is a test for.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added WorkItem attributes to all 7 test methods with the issue URL. (commit: work in progress)

Copilot AI and others added 3 commits October 14, 2025 15:23
Added WorkItem attribute with issue URL to all 7 test methods as requested in code review.

Co-authored-by: CyrusNajmabadi <[email protected]>
@Copilot Copilot AI requested a review from CyrusNajmabadi October 14, 2025 15:24
Copilot finished work on behalf of CyrusNajmabadi October 14, 2025 15:24
@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review October 14, 2025 15:33
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner October 14, 2025 15:33
@CyrusNajmabadi
Copy link
Member

@dotnet/roslyn-compiler ptal.

@RikkiGibson
Copy link
Member

RikkiGibson commented Oct 14, 2025

I think I agree with Cyrus's interpretation of the spec.

I was suspicious that this scenario went from disallowed by spec to allowed by spec when the spec was rewritten in csharpstandard. I went and looked, and my interpretation is the same with both the old and new spec language.

See the old spec for ?.:

  • If T0 is a non-nullable value type, then the type of E is T0?, and the meaning of E is the same as

    ((object)P == null) ? (T0?)null : E1

    except that P is evaluated only once.

  • Otherwise the type of E is T0, and the meaning of E is the same as

    ((object)P == null) ? null : E1

It looks like past interpretations were that pointers are non-nullable value types. On that reasoning, it's invalid for a conditional-access's value to be of a pointer type, because int*? isn't valid.

However, both the old and new specs say that pointers are not value types:

Pointer types are a separate category of types. Unlike reference types and value types, pointer types do not inherit from object and no conversions exist between pointer types and object.

I think the motivation for actually making the change is low. I don't think it's clear that the spec authors had pointers in mind when the ?. feature was being drafted. But, if there isn't a strong pushback from the rest of the team, I don't have a problem with moving forward with this, as a bug-fix level change for a niche scenario.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support null-conditional operator when the return value is a pointer type

4 participants