Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
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
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ internal static string PairToString(object key, object value)
// and IReadOnlyDictionary<TKey, TValue>.
[Serializable]
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public struct KeyValuePair<TKey, TValue>
public readonly struct KeyValuePair<TKey, TValue>
Copy link
Member

Choose a reason for hiding this comment

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

I'm working on the APICompat checks work but I'm not sure if this a compatible change. @jkotas @stephentoub is this an API compatible change?

Copy link
Member

Choose a reason for hiding this comment

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

Marking struct as readonly should be compatible change. The other direction (removing readonly on a struct) is not a compatible change.

cc @VSadov

Copy link
Member

Choose a reason for hiding this comment

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

Yes this is a compatible change.

{
private TKey key; // Do not rename (binary serialization)
private TValue value; // Do not rename (binary serialization)
private readonly TKey key; // Do not rename (binary serialization)
private readonly TValue value; // Do not rename (binary serialization)

public KeyValuePair(TKey key, TValue value)
{
Expand Down
4 changes: 2 additions & 2 deletions src/mscorlib/shared/System/DateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ namespace System
[StructLayout(LayoutKind.Auto)]
[Serializable]
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public partial struct DateTime : IComparable, IFormattable, IConvertible, IComparable<DateTime>, IEquatable<DateTime>, ISerializable
public readonly partial struct DateTime : IComparable, IFormattable, IConvertible, IComparable<DateTime>, IEquatable<DateTime>, ISerializable
Copy link
Member

Choose a reason for hiding this comment

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

I see that you were not able to mark DateTimeOffset because of its internal implementation detail around binary serialization. It is pretty unfortunate. I am wondering whether the DateTimeOffset case would warrant using the Unsafe helper to cast away the readonly-liness, so that DateTimeOffset can be marked as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

Exactly. I started looking at fixing it, but decided to keep this PR just focused on the easy cases where implementation changes weren't necessary. There are probably also additional structs that could be made readonly if their fields were appropriately annotated; I only looked at / fixed a few in that category (e.g. DateTime, Nullable), but didn't do a full sweep looking for others; that can be done subsequently, either piecemeal or with an analyzer.

Copy link
Member

Choose a reason for hiding this comment

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

DateTimeOffset #19552

{
// Number of 100ns ticks per time unit
private const long TicksPerMillisecond = 10000;
Expand Down Expand Up @@ -135,7 +135,7 @@ public partial struct DateTime : IComparable, IFormattable, IConvertible, ICompa
// savings time hour and it is in daylight savings time. This allows distinction of these
// otherwise ambiguous local times and prevents data loss when round tripping from Local to
// UTC time.
private UInt64 _dateData;
private readonly UInt64 _dateData;

// Constructs a DateTime from a tick count. The ticks
// argument specifies the date as the number of 100-nanosecond intervals
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Diagnostics;
Expand Down Expand Up @@ -27,7 +27,7 @@ namespace System.Diagnostics.Tracing
#else
internal
#endif
unsafe struct PropertyValue
unsafe readonly struct PropertyValue
{
/// <summary>
/// Union of well-known value types, to avoid boxing those types.
Expand Down
2 changes: 1 addition & 1 deletion src/mscorlib/shared/System/Globalization/DaylightTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public DaylightTime(DateTime start, DateTime end, TimeSpan delta)
}

// Value type version of DaylightTime
internal struct DaylightTimeStruct
internal readonly struct DaylightTimeStruct
{
public DaylightTimeStruct(DateTime start, DateTime end, TimeSpan delta)
{
Expand Down
2 changes: 1 addition & 1 deletion src/mscorlib/shared/System/ParamsArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace System
{
internal struct ParamsArray
internal readonly struct ParamsArray
{
// Sentinel fixed-length arrays eliminate the need for a "count" field keeping this
// struct down to just 4 fields. These are only used for their "Length" property,
Expand Down
2 changes: 1 addition & 1 deletion src/mscorlib/shared/System/Reflection/ParameterModifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace System.Reflection
{
public struct ParameterModifier
public readonly struct ParameterModifier
{
private readonly bool[] _byRef;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace System.Runtime.CompilerServices
/// <summary>Provides an awaitable type that enables configured awaits on a <see cref="ValueTask{TResult}"/>.</summary>
/// <typeparam name="TResult">The type of the result produced.</typeparam>
[StructLayout(LayoutKind.Auto)]
public struct ConfiguredValueTaskAwaitable<TResult>
public readonly struct ConfiguredValueTaskAwaitable<TResult>
{
/// <summary>The wrapped <see cref="ValueTask{TResult}"/>.</summary>
private readonly ValueTask<TResult> _value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace System.Runtime.Serialization
{
public struct StreamingContext
public readonly struct StreamingContext
{
private readonly object _additionalContext;
private readonly StreamingContextStates _state;
Expand Down
2 changes: 1 addition & 1 deletion src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ namespace System.Threading.Tasks
/// </remarks>
[AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
[StructLayout(LayoutKind.Auto)]
public struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
public readonly struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
{
/// <summary>The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully.</summary>
internal readonly Task<TResult> _task;
Expand Down
2 changes: 1 addition & 1 deletion src/mscorlib/src/System/ArraySegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace System
// (ie, users could assign a new value to the old location).
[Serializable]
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public struct ArraySegment<T> : IList<T>, IReadOnlyList<T>
public readonly struct ArraySegment<T> : IList<T>, IReadOnlyList<T>
{
// Do not replace the array allocation with Array.Empty. We don't want to have the overhead of
// instantiating another generic type in addition to ArraySegment<T> for new type parameters.
Expand Down
2 changes: 1 addition & 1 deletion src/mscorlib/src/System/DateTime.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace System
{
public partial struct DateTime
public readonly partial struct DateTime
{
public static DateTime UtcNow
{
Expand Down
6 changes: 3 additions & 3 deletions src/mscorlib/src/System/Nullable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ namespace System
[Serializable]
[System.Runtime.Versioning.NonVersionable] // This only applies to field layout
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public struct Nullable<T> where T : struct
public readonly struct Nullable<T> where T : struct
{
private bool hasValue; // Do not rename (binary serialization)
internal T value; // Do not rename (binary serialization)
private readonly bool hasValue; // Do not rename (binary serialization)
internal readonly T value; // Do not rename (binary serialization)

[System.Runtime.Versioning.NonVersionable]
public Nullable(T value)
Expand Down
2 changes: 1 addition & 1 deletion src/mscorlib/src/System/Reflection/Emit/MethodBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,7 @@ internal virtual void EmitLocalSymInfo(ISymbolWriter symWriter)
/// Describes exception handler in a method body.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ExceptionHandler : IEquatable<ExceptionHandler>
internal readonly struct ExceptionHandler : IEquatable<ExceptionHandler>
{
// Keep in sync with unmanged structure.
internal readonly int m_exceptionClass;
Expand Down
12 changes: 6 additions & 6 deletions src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ namespace System.Runtime.CompilerServices
{
/// <summary>Provides an awaiter for awaiting a <see cref="System.Threading.Tasks.Task"/>.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
public struct TaskAwaiter : ICriticalNotifyCompletion, ITaskAwaiter
public readonly struct TaskAwaiter : ICriticalNotifyCompletion, ITaskAwaiter
{
// WARNING: Unsafe.As is used to access the generic TaskAwaiter<> as TaskAwaiter.
// Its layout must remain the same.
Expand Down Expand Up @@ -320,7 +320,7 @@ private static Action OutputWaitEtwEvents(Task task, Action continuation)

/// <summary>Provides an awaiter for awaiting a <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, ITaskAwaiter
public readonly struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, ITaskAwaiter
{
// WARNING: Unsafe.As is used to access TaskAwaiter<> as the non-generic TaskAwaiter.
// Its layout must remain the same.
Expand Down Expand Up @@ -409,7 +409,7 @@ internal interface IConfiguredValueTaskAwaiter

/// <summary>Provides an awaitable object that allows for configured awaits on <see cref="System.Threading.Tasks.Task"/>.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
public struct ConfiguredTaskAwaitable
public readonly struct ConfiguredTaskAwaitable
{
/// <summary>The task being awaited.</summary>
private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter m_configuredTaskAwaiter;
Expand All @@ -434,7 +434,7 @@ public ConfiguredTaskAwaitable.ConfiguredTaskAwaiter GetAwaiter()

/// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable"/>.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion, IConfiguredTaskAwaiter
public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion, IConfiguredTaskAwaiter
{
// WARNING: Unsafe.As is used to access the generic ConfiguredTaskAwaiter as this.
// Its layout must remain the same.
Expand Down Expand Up @@ -500,7 +500,7 @@ public void GetResult()

/// <summary>Provides an awaitable object that allows for configured awaits on <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
public struct ConfiguredTaskAwaitable<TResult>
public readonly struct ConfiguredTaskAwaitable<TResult>
{
/// <summary>The underlying awaitable on whose logic this awaitable relies.</summary>
private readonly ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter m_configuredTaskAwaiter;
Expand All @@ -524,7 +524,7 @@ public ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter GetAwaiter()

/// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable{TResult}"/>.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion, IConfiguredTaskAwaiter
public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion, IConfiguredTaskAwaiter
{
// WARNING: Unsafe.As is used to access this as the non-generic ConfiguredTaskAwaiter.
// Its layout must remain the same.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace System.Runtime.CompilerServices

/// <summary>Provides an awaitable context for switching into a target environment.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
public struct YieldAwaitable
public readonly struct YieldAwaitable
{
/// <summary>Gets an awaiter for this <see cref="YieldAwaitable"/>.</summary>
/// <returns>An awaiter for this awaitable.</returns>
Expand All @@ -46,7 +46,7 @@ public struct YieldAwaitable

/// <summary>Provides an awaiter that switches into a target environment.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
public struct YieldAwaiter : ICriticalNotifyCompletion
public readonly struct YieldAwaiter : ICriticalNotifyCompletion
{
/// <summary>Gets whether a yield is not required.</summary>
/// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
Expand Down
2 changes: 1 addition & 1 deletion src/mscorlib/src/System/Threading/CancellationToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace System.Threading
/// </para>
/// </remarks>
[DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")]
public struct CancellationToken
public readonly struct CancellationToken
{
private readonly static Action<object> s_actionToActionObjShunt = obj => ((Action)obj)();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace System.Threading
/// <remarks>
/// To unregister a callback, dispose the corresponding Registration instance.
/// </remarks>
public struct CancellationTokenRegistration : IEquatable<CancellationTokenRegistration>, IDisposable
public readonly struct CancellationTokenRegistration : IEquatable<CancellationTokenRegistration>, IDisposable
{
private readonly long _id;
private readonly CancellationTokenSource.CallbackNode _node;
Expand Down
2 changes: 1 addition & 1 deletion src/mscorlib/src/System/TimeZoneInfo.TransitionTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace System
public sealed partial class TimeZoneInfo
{
[Serializable]
public struct TransitionTime : IEquatable<TransitionTime>, ISerializable, IDeserializationCallback
public readonly struct TransitionTime : IEquatable<TransitionTime>, ISerializable, IDeserializationCallback
{
private readonly DateTime _timeOfDay;
private readonly byte _month;
Expand Down