Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address feedback from numeric IntPtr feature review #61418

Merged
merged 10 commits into from
May 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,41 @@
# This document lists known breaking changes in Roslyn after .NET 6 all the way to .NET 7.

## Checked operators on System.IntPtr and System.UIntPtr

***Introduced in .NET SDK 7.0.100, Visual Studio 2022 version 17.3.***

When the platform supports __numeric__ `IntPtr` and `UIntPtr` types (as indicated by the presence of
`System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr`) the built-in operators from `nint`
and `nuint` apply to those underlying types.
This means that on such platforms, `IntPtr` and `UIntPtr` have built-in `checked` operators, which
can now throw when an overflow occurs.

```csharp
IntPtr M(IntPtr x, int y)
{
checked
{
return x + y; // may now throw
}
}

unsafe IntPtr M2(void* ptr)
{
return checked((IntPtr)ptr); // may now throw
}
```

Possible workarounds are:

1. Specify `unchecked` context
2. Downgrade to a platform/TFM without numeric `IntPtr`/`UIntPtr` types

Also, implicit conversions between `IntPtr`/`UIntPtr` and other numeric types are treated as standard
conversions on such platforms. This can affect overload resolution in some cases.

## Nameof operator in attribute on method or local function

***Introduced in .NET SDK 7.0.400, Visual Studio 2022 version 17.3.***
***Introduced in .NET SDK 6.0.400, Visual Studio 2022 version 17.3.***

When the language version is C# 11 or later, a `nameof` operator in an attribute on a method
brings the type parameters of that method in scope. The same applies for local functions.
Expand Down
147 changes: 147 additions & 0 deletions src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests;
Expand Down Expand Up @@ -10065,6 +10066,152 @@ public static void M9(nuint{{s2Nullable}} x) { }
CompileAndVerify(comp, expectedOutput: expected);
}

[Fact]
public void RetargetingFromNonNumericToNumericIntPtrCorlib()
{
string lib_cs = """
public class Base
{
public virtual nint M() => 0;
}
""";
var libComp = CreateEmptyCompilation(lib_cs, references: new[] { MscorlibRef_v20 }, assemblyName: "lib");
libComp.VerifyDiagnostics();

string source = """
public class Derived : Base
{
public override nint M() => 0;
}
""";
var comp = CreateNumericIntPtrCompilation(source, references: new[] { libComp.ToMetadataReference(), MscorlibRefWithoutSharingCachedSymbols });
comp.VerifyDiagnostics(
// warning CS1701: Assuming assembly reference 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' used by 'lib' matches identity 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' of 'mscorlib', you may need to supply runtime policy
Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin).WithArguments("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "lib", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "mscorlib").WithLocation(1, 1),
// warning CS1701: Assuming assembly reference 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' used by 'lib' matches identity 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' of 'mscorlib', you may need to supply runtime policy
Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin).WithArguments("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "lib", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "mscorlib").WithLocation(1, 1),
// warning CS1701: Assuming assembly reference 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' used by 'lib' matches identity 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' of 'mscorlib', you may need to supply runtime policy
Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin).WithArguments("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "lib", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "mscorlib").WithLocation(1, 1),
// warning CS1701: Assuming assembly reference 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' used by 'lib' matches identity 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' of 'mscorlib', you may need to supply runtime policy
Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin).WithArguments("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "lib", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "mscorlib").WithLocation(1, 1)
);

var baseM = (RetargetingMethodSymbol)comp.GlobalNamespace.GetMember("Base.M");
var baseNint = (PENamedTypeSymbol)baseM.ReturnType;

var derivedM = (MethodSymbol)comp.GlobalNamespace.GetMember("Derived.M");
var derivedNint = (PENamedTypeSymbol)derivedM.ReturnType;

Assert.Equal("nint", derivedNint.ToTestDisplayString());
Assert.Same(baseNint, derivedNint);
}

[Fact]
public void RetargetingFromNumericIntPtrToNonNumericCorlib()
{
string lib_cs = """
public class Base
{
public virtual nint M() => 0;
}
""";
var libComp = CreateNumericIntPtrCompilation(lib_cs, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, assemblyName: "lib");
libComp.VerifyDiagnostics();

string source = """
public class Derived : Base
{
public override nint M() => 0;
}
""";
var comp = CreateEmptyCompilation(source, references: new[] { libComp.ToMetadataReference(), MscorlibRef_v46 });
comp.VerifyDiagnostics();

var baseM = (RetargetingMethodSymbol)comp.GlobalNamespace.GetMember("Base.M");
var baseNint = (PENamedTypeSymbol)baseM.ReturnType;

var derivedM = (MethodSymbol)comp.GlobalNamespace.GetMember("Derived.M");
var derivedNint = (NativeIntegerTypeSymbol)derivedM.ReturnType;

Assert.Equal("System.IntPtr", baseNint.ToTestDisplayString());
Assert.Equal("nint", derivedNint.ToTestDisplayString());
Assert.Same(baseNint, derivedNint.UnderlyingNamedType);
}

[Fact]
public void UnsignedRightShift()
{
string source = """
public class C
{
nint M1(nint x, int count) => x >>> count;
nuint M2(nuint x, int count) => x >>> count;
nint M3(nint x, int count) => checked(x >>> count);
nuint M4(nuint x, int count) => checked(x >>> count);

System.IntPtr M5(System.IntPtr x, int count) => x >>> count;
System.UIntPtr M6(System.UIntPtr x, int count) => x >>> count;
}
""";
var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols });
comp.VerifyDiagnostics();
var verifier = CompileAndVerify(comp);
var expectedIL = @"
{
// Code size 4 (0x4)
.maxstack 2
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: shr.un
IL_0003: ret
}
";
verifier.VerifyIL("C.M1", expectedIL);
verifier.VerifyIL("C.M2", expectedIL);
verifier.VerifyIL("C.M3", expectedIL);
verifier.VerifyIL("C.M4", expectedIL);
verifier.VerifyIL("C.M5", expectedIL);
verifier.VerifyIL("C.M6", expectedIL);
}

[Fact]
public void OverflowPointerConversion()
{
// Breaking change
string source = """
using System;
class C
{
public unsafe static void Main()
{
void* ptr = (void*)ulong.MaxValue;

try
{
IntPtr i = checked((IntPtr)ptr);
}
catch (System.OverflowException)
{
Console.Write("OVERFLOW ");
}

IntPtr j = unchecked((IntPtr)ptr);
if (j == (IntPtr)(-1))
{
Console.Write("RAN");
}
}
}
""";
var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeReleaseExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "OVERFLOW RAN", verify: Verification.Skipped);

comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "RAN", verify: Verification.Skipped);
}

[Fact]
public void VariousMembersToDecodeOnRuntimeFeatureType()
{
Expand Down