diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md index 6ef16fbdca75a..e4a592fca6c1f 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md @@ -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. diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs index 8d775a22686eb..0d3a25ea598aa 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs @@ -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; @@ -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() {