Skip to content

Commit

Permalink
Address feedback from numeric IntPtr feature review (#61418)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored May 20, 2022
1 parent 1814273 commit 1386114
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 1 deletion.
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

0 comments on commit 1386114

Please sign in to comment.