Skip to content
Draft
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
117 changes: 0 additions & 117 deletions .github/CODEOWNERS

This file was deleted.

2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@

<!-- Packaging -->
<PropertyGroup>
<GitHubRepositoryName>runtime</GitHubRepositoryName>
<GitHubRepositoryName>runtimelab</GitHubRepositoryName>
<RepositoryUrl>https://github.com/dotnet/$(GitHubRepositoryName)</RepositoryUrl>
<PackageProjectUrl>https://dot.net</PackageProjectUrl>
<Owners>microsoft,dotnetframework</Owners>
Expand Down
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageVersionNet8>8.0.$([MSBuild]::Add($([System.Version]::Parse('$(PackageVersionNet9)').Build),11))</PackageVersionNet8>
<PackageVersionNet7>7.0.20</PackageVersionNet7>
<PackageVersionNet6>6.0.36</PackageVersionNet6>
<PreReleaseVersionLabel>rc</PreReleaseVersionLabel>
<PreReleaseVersionLabel>unsafe</PreReleaseVersionLabel>
<PreReleaseVersionIteration>1</PreReleaseVersionIteration>
<!-- Enable to remove prerelease label. -->
<StabilizePackageVersion Condition="'$(StabilizePackageVersion)' == ''">false</StabilizePackageVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<FeaturePortableThreadPool Condition="'$(TargetsBrowser)' != 'true'">true</FeaturePortableThreadPool>
<FeaturePortableTimer Condition="'$(TargetsBrowser)' != 'true'">true</FeaturePortableTimer>
<FeatureSingleThread Condition="'$(TargetsBrowser)' == 'true'">true</FeatureSingleThread>
<EnableUnsafeAnalyzer>true</EnableUnsafeAnalyzer>
</PropertyGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions src/libraries/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
<Nullable Condition="'$(Nullable)' == '' and '$(IsTestProject)' == 'true'">annotations</Nullable>
<!-- AOT compatibility is enabled by default for src/ref projects. -->
<IsAotCompatible Condition="'$(IsAotCompatible)' == '' and ('$(IsSourceProject)' == 'true' or '$(IsReferenceAssemblyProject)' == 'true')">true</IsAotCompatible>
<EnableUnsafeAnalyzer>true</EnableUnsafeAnalyzer>
</PropertyGroup>

<!-- Set up common paths -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\CodeAnalysis\RequiresAssemblyFilesAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\CodeAnalysis\RequiresDynamicCodeAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\CodeAnalysis\RequiresUnreferencedCodeAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\CodeAnalysis\RequiresUnsafeAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\CodeAnalysis\SetsRequiredMembersAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\CodeAnalysis\StringSyntaxAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\CodeAnalysis\SuppressMessageAttribute.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ public override T[] Rent(int minimumLength)
SharedArrayPoolThreadLocalArray[]? tlsBuckets = t_tlsBuckets;
if (tlsBuckets is not null && (uint)bucketIndex < (uint)tlsBuckets.Length)
{
buffer = Unsafe.As<T[]>(tlsBuckets[bucketIndex].Array);
unsafe
{
buffer = Unsafe.As<T[]>(tlsBuckets[bucketIndex].Array);
Copy link
Member

Choose a reason for hiding this comment

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

We could remove the unsafe here and just cast to T[] here, right?

The only reason this isn't already T[] and that it's using System.Array instead is to avoid the generic code size increase for the SharedArrayPoolThreadLocalArray struct, but maybe that's something we could track addressing as well if the cast is too expensive.

Copy link
Member

Choose a reason for hiding this comment

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

Right, this has perf fine-tuned using unsafe code. Number of other cases in this PR are in the same category.

Copy link
Member

Choose a reason for hiding this comment

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

This is a good case-study for the annotation guidelines. This is safe because we manually ensure that we only assign T[] into Array field. Should the Array field and all places that assign to it be marked unsafe as well?

}
if (buffer is not null)
{
tlsBuckets[bucketIndex].Array = null;
Expand All @@ -79,7 +82,10 @@ public override T[] Rent(int minimumLength)
SharedArrayPoolPartitions? b = perCoreBuckets[bucketIndex];
if (b is not null)
{
buffer = Unsafe.As<T[]>(b.TryPop());
unsafe
{
buffer = Unsafe.As<T[]>(b.TryPop());
}
if (buffer is not null)
{
if (log.IsEnabled())
Expand Down
10 changes: 8 additions & 2 deletions src/libraries/System.Private.CoreLib/src/System/Delegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,15 @@ public TDelegate Current
public bool MoveNext()
{
int index = _index + 1;
if ((_current = Unsafe.As<TDelegate>(_delegate?.TryGetAt(index))) == null)
unsafe
{
return false;
unsafe
Copy link
Member

Choose a reason for hiding this comment

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

This one is really, really unsafe I guess :)

Copy link
Member Author

Choose a reason for hiding this comment

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

lol not sure what happened there

{
if ((_current = Unsafe.As<TDelegate>(_delegate?.TryGetAt(index))) == null)
{
return false;
}
}
}
_index = index;
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Diagnostics.CodeAnalysis
{
/// <summary>
/// Indicates that the specified method requires dynamic access to code that is not referenced
/// statically, for example through <see cref="Reflection"/>.
/// </summary>
/// <remarks>
/// This allows tools to understand which methods are unsafe to call when removing unreferenced
/// code from an application.
/// </remarks>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)]
internal sealed class RequiresUnsafeAttribute : Attribute
Copy link
Member

Choose a reason for hiding this comment

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

Since we know some version of this attribute is going to be required, should we go ahead and take it to API review so we can land on the name (even if the exact constructors it contains may change over time)?

There's been a few names that have been floated around such as RequiresUnsafe (closest prior is RequiresUnreferencedCode) or UnsafeCallersOnly (closest prior is UnmanagedCallersOnly). There may be other ideas/opinions that more correctly conveys "this member is unsafe" as well.

{
}
}
12 changes: 10 additions & 2 deletions src/libraries/System.Private.CoreLib/src/System/Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,11 @@ public Span<T> Span
{
// Special-case string since it's the most common for ROM<char>.

refToReturn = ref Unsafe.As<char, T>(ref ((string)tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
unsafe
{
refToReturn = ref Unsafe.As<char, T>(ref ((string)tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
}
}
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
Expand Down Expand Up @@ -379,6 +382,11 @@ public Span<T> Span
/// <exception cref="ArgumentException">
/// An instance with nonprimitive (non-blittable) members cannot be pinned.
/// </exception>
/// <remarks>
/// To use this method safely, the target <see cref="Memory{T}"/> must not be torn while the
/// returned <see cref="MemoryHandle"/> is in use.
/// </remarks>
[RequiresUnsafe]
public unsafe MemoryHandle Pin()
{
// Just like the Span property getter, we have special support for a mutable Memory<char>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5905,7 +5905,10 @@ internal SpanSplitEnumerator(ReadOnlySpan<T> source, ReadOnlySpan<T> separators)
_source = source;
if (typeof(T) == typeof(char) && separators.Length == 0)
{
_searchValues = Unsafe.As<SearchValues<T>>(string.SearchValuesStorage.WhiteSpaceChars);
unsafe
{
_searchValues = Unsafe.As<SearchValues<T>>(string.SearchValuesStorage.WhiteSpaceChars);
}
_splitMode = SpanSplitEnumeratorMode.SearchValues;
}
else
Expand Down
23 changes: 16 additions & 7 deletions src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,11 @@ public ReadOnlySpan<T> Span
{
// Special-case string since it's the most common for ROM<char>.

refToReturn = ref Unsafe.As<char, T>(ref ((string)tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
unsafe
{
refToReturn = ref Unsafe.As<char, T>(ref ((string)tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
}
}
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
Expand All @@ -223,8 +226,11 @@ public ReadOnlySpan<T> Span
// 'tmpObject is T[]' below also handles things like int[] <-> uint[] being convertible
Debug.Assert(tmpObject is T[]);

refToReturn = ref MemoryMarshal.GetArrayDataReference(Unsafe.As<T[]>(tmpObject));
lengthOfUnderlyingSpan = Unsafe.As<T[]>(tmpObject).Length;
unsafe
{
refToReturn = ref MemoryMarshal.GetArrayDataReference(Unsafe.As<T[]>(tmpObject));
lengthOfUnderlyingSpan = Unsafe.As<T[]>(tmpObject).Length;
}
}
else
{
Expand All @@ -235,9 +241,12 @@ public ReadOnlySpan<T> Span
// constructor or other public API which would allow such a conversion.

Debug.Assert(tmpObject is MemoryManager<T>);
Span<T> memoryManagerSpan = Unsafe.As<MemoryManager<T>>(tmpObject).GetSpan();
refToReturn = ref MemoryMarshal.GetReference(memoryManagerSpan);
lengthOfUnderlyingSpan = memoryManagerSpan.Length;
unsafe
{
Span<T> memoryManagerSpan = Unsafe.As<MemoryManager<T>>(tmpObject).GetSpan();
refToReturn = ref MemoryMarshal.GetReference(memoryManagerSpan);
lengthOfUnderlyingSpan = memoryManagerSpan.Length;
}
}

// If the Memory<T> or ReadOnlyMemory<T> instance is torn, this property getter has undefined behavior.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,10 @@ internal bool Remove(TKey key, [MaybeNullWhen(false)] out TValue value)
if (entryIndex != -1)
{
RemoveIndex(entryIndex);
value = Unsafe.As<TValue>(valueObject!);
unsafe
{
value = Unsafe.As<TValue>(valueObject!);
}
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public static int SizeOf<T>()
// Mono:As
[NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[RequiresUnsafe]
[return: NotNullIfNotNull(nameof(o))]
public static T? As<T>(object? o) where T : class?
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,11 +560,14 @@ private bool TryFindMatch(ReadOnlySpan<char> span, ref char searchSpace, Vector1
object? bucket = _buckets[candidateOffset];
Debug.Assert(bucket is not null);

if (TBucketized.Value
? StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string[]>(bucket))
: StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string>(bucket)))
unsafe
{
return true;
if (TBucketized.Value
? StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string[]>(bucket))
: StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string>(bucket)))
{
return true;
}
}

candidateMask = BitOperations.ResetLowestSetBit(candidateMask);
Expand Down Expand Up @@ -605,11 +608,14 @@ private bool TryFindMatch(ReadOnlySpan<char> span, ref char searchSpace, Vector2
object? bucket = _buckets[candidateOffset];
Debug.Assert(bucket is not null);

if (TBucketized.Value
? StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string[]>(bucket))
: StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string>(bucket)))
unsafe
{
return true;
if (TBucketized.Value
? StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string[]>(bucket))
: StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string>(bucket)))
{
return true;
}
}

candidateMask = BitOperations.ResetLowestSetBit(candidateMask);
Expand Down Expand Up @@ -650,11 +656,14 @@ private bool TryFindMatch(ReadOnlySpan<char> span, ref char searchSpace, Vector5
object? bucket = _buckets[candidateOffset];
Debug.Assert(bucket is not null);

if (TBucketized.Value
? StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string[]>(bucket))
: StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string>(bucket)))
unsafe
{
return true;
if (TBucketized.Value
? StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string[]>(bucket))
: StartsWith<TCaseSensitivity>(ref matchRef, lengthRemaining, Unsafe.As<string>(bucket)))
{
return true;
}
}

candidateMask = BitOperations.ResetLowestSetBit(candidateMask);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2382,7 +2382,7 @@
{
Debug.Assert(obj is Task);
// Only used privately to pass directly to EC.Run
Unsafe.As<Task>(obj).InnerInvoke();

Check failure on line 2385 in src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs

View check run for this annotation

Azure Pipelines / runtimelab (Build linux-x64 checked)

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs#L2385

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs(2385,13): error IL3059: (NETCORE_ENGINEERING_TELEMETRY=Build) Using method 'System.Runtime.CompilerServices.Unsafe.As<T>(Object)' which has 'RequiresUnsafeAttribute' can break functionality in environments that do not support unsafe code.

Check failure on line 2385 in src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs

View check run for this annotation

Azure Pipelines / runtimelab (Build linux-x64 release)

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs#L2385

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs(2385,13): error IL3059: (NETCORE_ENGINEERING_TELEMETRY=Build) Using method 'System.Runtime.CompilerServices.Unsafe.As<T>(Object)' which has 'RequiresUnsafeAttribute' can break functionality in environments that do not support unsafe code.

Check failure on line 2385 in src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs

View check run for this annotation

Azure Pipelines / runtimelab

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs#L2385

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs(2385,13): error IL3059: (NETCORE_ENGINEERING_TELEMETRY=Build) Using method 'System.Runtime.CompilerServices.Unsafe.As<T>(Object)' which has 'RequiresUnsafeAttribute' can break functionality in environments that do not support unsafe code.
};

/// <summary>
Expand Down Expand Up @@ -6743,7 +6743,10 @@
// since if argument was strongly-typed as an array, it would have bound to the array-based overload.
if (tasks.GetType() == typeof(List<TTask>))
{
return WhenAnyCore((ReadOnlySpan<TTask>)CollectionsMarshal.AsSpan(Unsafe.As<List<TTask>>(tasks)));
unsafe
{
return WhenAnyCore((ReadOnlySpan<TTask>)CollectionsMarshal.AsSpan(Unsafe.As<List<TTask>>(tasks)));
}
}
if (tasks is TTask[] tasksAsArray)
{
Expand Down
Loading
Loading