p_chinのおっぱいブログ

UnityとPerlなど

関数の宣言まわりで地雷踏んでUnityのmonoでInternalCompilerErrorが出てしまったのを解決した話

問題

以下の様なコードをUnityのmonoでコンパイルしたらInternalCompilerErrorを出してしまった。

コード

public class Bomb
{
    public void Explosion(float radius = 10f){}
    public void Explosion(Vector3 worldPos, float radius = 10f){}
}

public class BomberMan
{
    public void ExecuteExplosion()
    {
        var bomb = new Bomb();
        bomb.Explosion(radius: 100f);
    }
}

InternalCompilerErrorの一部

Internal compiler error. See the console log for more information. output 

-----
UnityEngine.dllとかで握りつぶされてた大量のwarningが書かれてた
-----

Unhandled Exception: Mono.CSharp.InternalErrorException: VerifyArgumentsCompat didn't find any problem with rejected candidate MethodBuilder [{エラーが出てる関数名}]
  at Mono.CSharp.MethodGroupExpr.OverloadResolve (Mono.CSharp.ResolveContext ec, Mono.CSharp.Arguments& Arguments, Boolean may_fail, Location loc) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Invocation.DoResolveOverload (Mono.CSharp.ResolveContext ec) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Invocation.DoResolve (Mono.CSharp.ResolveContext ec) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Expression.Resolve (Mono.CSharp.ResolveContext ec, ResolveFlags flags) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Expression.Resolve (Mono.CSharp.ResolveContext ec) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Argument.Resolve (Mono.CSharp.ResolveContext ec) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Arguments.Resolve (Mono.CSharp.ResolveContext ec, System.Boolean& dynamic) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Invocation.DoResolve (Mono.CSharp.ResolveContext ec) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Expression.Resolve (Mono.CSharp.ResolveContext ec, ResolveFlags flags) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Expression.Resolve (Mono.CSharp.ResolveContext ec) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.ExpressionStatement.ResolveStatement (Mono.CSharp.BlockContext ec) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.StatementExpression.Resolve (Mono.CSharp.BlockContext ec) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.Block.Resolve (Mono.CSharp.BlockContext ec) [0x00000] in <filename unknown>:0 
  at Mono.CSharp.ToplevelBlock.Resolve (Mono.CSharp.FlowBranching parent, Mono.CSharp.BlockContext rc, Mono.CSharp.ParametersCompiled ip, IMethodData md) [0x00000] in <filename unknown>:0 

原因

以下の条件が重なるとダメっぽい

  • 引数をOptionalにしてる
  • ex. float radius = 10f
  • オーバーロードした関数が元の関数と引数の順番が違う
  • 今回の場合はオーバーロードする時に『第一引数のworldPosをOptionalにしたくない』という理由で引数の順番を変えてしまっていた様子
    • (前の引数がOptinalだった場合には以降の引数もOptinoalにしないとコンパイルエラーになるので)
  • 呼び出し側が引数のパラメータ名付きで引数を送ってる
    • ex. bomb.Explosion(radius: 100f);

解決方法

以下のどれかの方法を取れば解決する

  • BomberMan.ExecuteExplosion()bomb.Explosion()の引数指定にパラメータ名を添えるのをやめる
  • Bomb.Explosionの引数であるradiusをOptionalにしない
public class Bomb
{
    // `Bomb.Explosion`の引数である`radius`をOptionalにしない
    public void Explosion(float radius){}
    public void Explosion(Vector3 worldPos, float radius){}
}

public class BomberMan
{
    public void ExecuteExplosion()
    {
        var bomb = new Bomb();

        // `BomberMan.ExecuteExplosion()`で`bomb.Explosion()`の引数指定にパラメータ名を添えるのをやめる
        bomb.Explosion(100);
    }
}

追加情報

@shogo82148 が調べてくれてた

shogo82148.github.io

monoのrepositoryでは、既に修正が入ってるっぽい https://github.com/mono/mono/commit/7ad366c25a7fc180a57dcf88f28064ec3db619b6

でも、Unityの中のmonoにはこの修正はまだ取り込まれていない様だった https://github.com/Unity-Technologies/mono/blob/unity-staging/mcs/mcs/ecore.cs#L3794

StackOverFlowだとここら辺の情報が今回の件に該当しそう http://stackoverflow.com/questions/4495108/mono-named-optional-parameters-compiler-bug