C#
C#7.2

C#7.2 コンパイラのバグぽいもの

デフォルト引数にin で死ぬ

using System;

namespace ConsoleApp1 {
    class Program {
        static void Test( in double x = 1 , in string y = "" ) => Console.WriteLine( y );
        static void Main() {
            Test( 10 );
            Console.ReadLine();
        }
    }
}

私の環境で実行するとこんな感じにバグる。
AccessViolationException吐く場合もある。(触っちゃいけないメモリにアクセスしようとする可能性がある)
image.png

これなら問題ない
static void Test( double x = 1 , in string y = "" ) => Console.WriteLine( y );

string以外、例えば、int(値型)にすると、値型なのにNullReferenceExceptionだと言われる。

image.png

何が起きているのか?

Test( 10 );というのは、実際には、こんなふうにコンパイラによって展開される。(…はずである)
inは、結局のところref扱いされている。refというのは、ローカル変数の参照を渡すもの。

正常ならこんな感じに展開される
static void Main() {
    int a = 1;     // x
    string b = ""; // y
    Test( ref a , ref b );
}
問題が発生してあるケース
static void Main() {
    int a = 1;     // x
    Test( ref a , "" );
}

即値を渡すとまずいところに即値を渡しているため、参照先が不正、どこにも実体が無い状態となってしまう。

そもそも、次のコードは、コンパイラエラーになるわけでin=refである以上、コンパイル通るのがおかしい。

Test( ref int x = 100 );

まあでもinなんだから通っても構わないという意見もある。

名前付き引数でもバグる。
class Program {
    static void Test( in int x = 646346 , in int y = 100 ) => Console.WriteLine( x );
    static void Main() {
        Test( y : 10 );
        Console.ReadLine();
    }
}

x = 646346の部分を即値として渡してしまい、すなわちアドレス(646346)を読みに行こうとする。

参照型のthisinに渡してフィールドを読み取ると死ぬ。

いろいろ検証した結果...別にvirtualメンバーと関係なかった
image.png

問題のコード
using System;

namespace ConsoleApp1 {
    sealed class A {
        public string Value = "";
        public void Run() => show( this );

        static void show( in A a ) => Console.WriteLine( a.Value ); 
                         // ↑inでthisを受けることでメモリ破壊されている?
                         // * thisを一時変数にコピーするなどした場合は、発生しない。
    }

    class Program {
        static void Main() {
            var a = new A();
            a.Run();
            Console.ReadLine();
        }
    }
}

参照型の this を in で受けるメソッドに渡した上で、フィールドにアクセスするとAccessViolationExceptionExecutionEngineExceptionで死にます。

値型の場合は、フィールドの値がバグってたりする。

1234と出力されるはずが278920と出力される(実行環境によって異なるはず)
using System;

namespace ConsoleApp1 {
    sealed class A {
        public int Value = 1234;
        public void Run() => show( this );
        static void show( in A a ) => Console.WriteLine( a.Value ); 
    }

    class Program {
        static void Main() {
            new A().Run();
            Console.ReadLine();
        }
    }
}

これも実際のところ、こんなふうにrefで受け取るわけで...

void show( ref A a ) => Console.WriteLine( a.Value );
show( ref this );

のように呼ぶわけで、これはコンパイルエラーになるわけだから、in thisで通るのがおかしい。

外部アセンブリで演算子のオーバーロードにinを使うとコンパイルエラーになる。

using System;

namespace ConsoleApp1 {
    public struct Hoge {
        public Hoge( int value ) => this.Value = value;
        public int Value;
        public static implicit operator int( in Hoge source ) => source.Value;
    }

    class Program {
        static void Main() {
            Hoge x = new Hoge( 100 );
            if( x == new Hoge(200 ) )
                Console.WriteLine();
            Console.ReadLine();
        }
    }
}

同一アセンブリ内であれば、問題ないがHogeを外部アセンブリに移動するとコンパイルエラーになる。

エラー一覧には出ず、出力に出てくる。
error CS0019: 演算子 '==' を 'Hoge' と 'Hoge' 型のオペランドに適用することはできません

キャスト演算子だけではなく、どのオペレータでも発生する。

恐らく近々修正が入るだろうけど、修正されるまで気をつけるべし...