Skip to content

Public API proposal: unsafe evolution #82791

@jjonescz

Description

@jjonescz

Background and Motivation

Public API for compiler implementation of language feature unsafe evolution.

Proposed API

 namespace Microsoft.CodeAnalysis
 {
      public interface ISymbol
      {
+          /// <summary>
+          /// Whether this symbol uses the updated memory safety rules and is considered <see langword="unsafe"/> under those rules.
+          /// </summary>
+          [Experimental(RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = "https://github.com/dotnet/roslyn/issues/82789")]
+          bool RequiresUnsafe { get; }
      }
 }

 namespace Microsoft.CodeAnalysis.CSharp
 {
     public sealed class CSharpCompilationOptions
     {
+        /// <summary>
+        /// Memory safety rules.
+        /// <list type="bullet">
+        /// <item><c>0</c> - legacy rules</item>
+        /// <item><c>2</c> - updated rules (support for <c>[RequiresUnsafe]</c> members)</item>
+        /// </list>
+        /// </summary>
+        [Experimental(RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = "https://github.com/dotnet/roslyn/issues/82789")]
+        public int MemorySafetyRules { get; private set; }
 
+        [Experimental(RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = "https://github.com/dotnet/roslyn/issues/82789")]
+        public CSharpCompilationOptions(
+            OutputKind outputKind,
+            bool reportSuppressedDiagnostics = false,
+            string? moduleName = null,
+            string? mainTypeName = null,
+            string? scriptClassName = null,
+            IEnumerable<string>? usings = null,
+            OptimizationLevel optimizationLevel = OptimizationLevel.Debug,
+            bool checkOverflow = false,
+            bool allowUnsafe = false,
+            string? cryptoKeyContainer = null,
+            string? cryptoKeyFile = null,
+            ImmutableArray<byte> cryptoPublicKey = default,
+            bool? delaySign = null,
+            Platform platform = Platform.AnyCpu,
+            ReportDiagnostic generalDiagnosticOption = ReportDiagnostic.Default,
+            int warningLevel = Diagnostic.DefaultWarningLevel,
+            IEnumerable<KeyValuePair<string, ReportDiagnostic>>? specificDiagnosticOptions = null,
+            bool concurrentBuild = true,
+            bool deterministic = false,
+            XmlReferenceResolver? xmlReferenceResolver = null,
+            SourceReferenceResolver? sourceReferenceResolver = null,
+            MetadataReferenceResolver? metadataReferenceResolver = null,
+            AssemblyIdentityComparer? assemblyIdentityComparer = null,
+            StrongNameProvider? strongNameProvider = null,
+            bool publicSign = false,
+            MetadataImportOptions metadataImportOptions = MetadataImportOptions.Public,
+            NullableContextOptions nullableContextOptions = NullableContextOptions.Disable,
+            int memorySafetyRules = 0);
 
-        public CSharpCompilationOptions(
-            OutputKind outputKind,
-            bool reportSuppressedDiagnostics = false,
-            string? moduleName = null,
-            string? mainTypeName = null,
-            string? scriptClassName = null,
-            IEnumerable<string>? usings = null,
-            OptimizationLevel optimizationLevel = OptimizationLevel.Debug,
-            bool checkOverflow = false,
-            bool allowUnsafe = false,
-            string? cryptoKeyContainer = null,
-            string? cryptoKeyFile = null,
-            ImmutableArray<byte> cryptoPublicKey = default,
-            bool? delaySign = null,
-            Platform platform = Platform.AnyCpu,
-            ReportDiagnostic generalDiagnosticOption = ReportDiagnostic.Default,
-            int warningLevel = Diagnostic.DefaultWarningLevel,
-            IEnumerable<KeyValuePair<string, ReportDiagnostic>>? specificDiagnosticOptions = null,
-            bool concurrentBuild = true,
-            bool deterministic = false,
-            XmlReferenceResolver? xmlReferenceResolver = null,
-            SourceReferenceResolver? sourceReferenceResolver = null,
-            MetadataReferenceResolver? metadataReferenceResolver = null,
-            AssemblyIdentityComparer? assemblyIdentityComparer = null,
-            StrongNameProvider? strongNameProvider = null,
-            bool publicSign = false,
-            MetadataImportOptions metadataImportOptions = MetadataImportOptions.Public,
-            NullableContextOptions nullableContextOptions = NullableContextOptions.Disable);
 
+        // 18.6 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
+        public CSharpCompilationOptions(
+            OutputKind outputKind,
+            bool reportSuppressedDiagnostics,
+            string? moduleName,
+            string? mainTypeName,
+            string? scriptClassName,
+            IEnumerable<string>? usings,
+            OptimizationLevel optimizationLevel,
+            bool checkOverflow,
+            bool allowUnsafe,
+            string? cryptoKeyContainer,
+            string? cryptoKeyFile,
+            ImmutableArray<byte> cryptoPublicKey,
+            bool? delaySign,
+            Platform platform,
+            ReportDiagnostic generalDiagnosticOption,
+            int warningLevel,
+            IEnumerable<KeyValuePair<string, ReportDiagnostic>>? specificDiagnosticOptions,
+            bool concurrentBuild,
+            bool deterministic,
+            XmlReferenceResolver? xmlReferenceResolver,
+            SourceReferenceResolver? sourceReferenceResolver,
+            MetadataReferenceResolver? metadataReferenceResolver,
+            AssemblyIdentityComparer? assemblyIdentityComparer,
+            StrongNameProvider? strongNameProvider,
+            bool publicSign,
+            MetadataImportOptions metadataImportOptions,
+            NullableContextOptions nullableContextOptions);
 
+        [Experimental(RoslynExperiments.PreviewLanguageFeatureApi, UrlFormat = "https://github.com/dotnet/roslyn/issues/82789")]
+        public CSharpCompilationOptions WithMemorySafetyRules(int version);
     }
 }

Plus we need the following (not a public library API, but a public contract nevertheless):

  • a command-line option for csc.exe: -memorysafetyrules:2,
  • a parameter for the Csc task and a corresponding MSBuild property: <MemorySafetyRules>2</MemorySafetyRules>.

Usage Examples

var options = new CSharpCompilationOptions(..., memorySafetyRules: 2);
options = options.WithMemorySafetyRules(0);

var compilation = new CSharpCompilation(..., options);
var m = compilation.GetSymbol("C.M");
var isUnsafe = m.RequiresUnsafe;

Alternative Designs

  • Also expose int UpdatedMemorySafetyRulesVersion as const/readonly field/property of CSharpCompilationOptions.
  • Also expose WithUpdatedMemorySafetyRules on CSharpCompilationOptions.
  • Turn ISymbol.RequiresUnsafe into a default interface member.
  • Only expose Csc.MemorySafetyRules but not /memorysafetyrules: (pass it via Features like InterceptorsNamespaces).

Risks

  • We cannot mark the command-line option and Csc task parameter as Experimental. Perhaps we should post-pone implementing them until the feature is close to being released.

Metadata

Metadata

Assignees

Labels

Area-CompilersConcept-APIThis issue involves adding, removing, clarification, or modification of an API.api-approvedAPI was approved in API review, it can be implemented
No fields configured for Feature.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions