dotnet/roslyn

There are some unnecessary defensive copies for deconstructors, implicit index & range, and foreach with explicit implementation

Open

#72.008 aperta il 8 feb 2024

Vedi su GitHub
 (3 commenti) (1 reazione) (0 assegnatari)C# (4257 fork)batch import
Area-CompilersBughelp wanted

Metriche repository

Star
 (20.414 star)
Metriche merge PR
 (Merge medio 6g 17h) (256 PR mergiate in 30 g)

Descrizione

When a member is marked as readonly, a defensive copy need not be made, since the member will not mutate the struct. These are some instances of where we could avoid more defensive copies.

Version Used:

Sharplab.io Also reproduces with compiler included in .NET 8.0.100 SDK.

Steps to Reproduce:

using System;
using System.Collections;
using System.Collections.Generic;

public class C
{
    public void M1(ref S1 value1, ref S2 value2)
    {
        int a, b;

        (a, b) = value1; //defensive copy (unnecessary, since value1 is mutable?)
        a = value1[^3]; //no defensive copy
        _ = value1[3..4]; //no defensive copy
        foreach (var x in value1) { } //no defensive copy

        (a, b) = value2; //defensive copy (unnecessary)
        a = value2[^3]; //no defensive copy
        _ = value2[3..4]; //no defensive copy
        foreach (var x in value2) { } //no defensive copy
    }

    public void M2(in S1 value1, in S2 value2)
    {
        int a, b;

        (a, b) = value1; //defensive copy
        a = value1[^3]; //defensive copy
        _ = value1[3..4]; //defensive copy
        foreach (var x in value1) { } //defensive copy

        (a, b) = value2; //defensive copy (unnecessary)
        a = value2[^3]; //defensive copy (unnecessary)
        _ = value2[3..4]; //defensive copy (unnecessary)
        foreach (var x in value2) { } //defensive copy (unnecessary)
    }
}

public struct S1 : IEnumerable<int>
{
    public void Deconstruct(out int a, out int b) => (a, b) = (1, 2);
    public int Length => 0;
    public int this[int a] => 0;
    public S1 Slice(int start, int length) => this;
    IEnumerator IEnumerable.GetEnumerator() => null!;
    IEnumerator<int> IEnumerable<int>.GetEnumerator() => null!;
}

public struct S2 : IEnumerable<int>
{
    public readonly void Deconstruct(out int a, out int b) => (a, b) = (1, 2);
    public readonly int Length => 0;
    public readonly int this[int a] => 0;
    public readonly S2 Slice(int start, int length) => this;
    readonly IEnumerator IEnumerable.GetEnumerator() => null!;
    readonly IEnumerator<int> IEnumerable<int>.GetEnumerator() => null!;
}

Link

Expected Behavior:

Members marked as "defensive copy (unnecessary)" do not emit a defensive copy.

Actual Behavior:

Members marked as "defensive copy (unnecessary)" do emit a defensive copy. The rest are correct.

Guida contributor