Skip to content

Tuple argument are handled inconsistently #2923

Description

@manofstick

For a function argument that is a tuple, when the argument is referenced by the argument name, you may or may not get the same tuple (as denoted by obj.ReferenceEquals)

Repro steps

Given

let source = [(1,1); (2,2)]

source
|> List.iter (fun outer -> 
    source
    |> List.exists (fun inner -> obj.ReferenceEquals (outer, inner)) // but Seq.exists fails!!
    |> fun found -> if not found then failwith "boom!") 

printfn "Hooray!"

The output is "Hooray!" but if the List.exists is change to Seq.exists then the exception condition is hit.

Expected behavior

The argument, when reference by name, should always be the original object.

Actual behavior

Sometimes, and I'm not sure the conditions under which it decides, the tuple is split and recreated in a new Tuple object. Even in optimized code. Neither of the exists function are marked as inline.

Known workarounds

Don't do reference equality checks on tuples!

Related information

Using Ilspy, it can be seen that for the List.exists case the following function object is created:

internal class clo@6-1 : FSharpFunc<Tuple<int, int>, bool>
{
	public Tuple<int, int> outer;

	[DebuggerNonUserCode, CompilerGenerated]
	internal clo@6-1(Tuple<int, int> outer)
	{
		this.outer = outer;
	}

	public override bool Invoke(Tuple<int, int> inner)
	{
		return object.ReferenceEquals(this.outer, inner);
	}
}

And for Seq.exists the following is created:

internal class main@9-1 : FSharpFunc<Tuple<int, int>, bool>
{
	public Tuple<int, int> outer;

	[DebuggerNonUserCode, CompilerGenerated]
	internal main@9-1(Tuple<int, int> outer)
	{
		this.outer = outer;
	}

	public override bool Invoke(Tuple<int, int> tupledArg)
	{
		int item = tupledArg.Item1;
		int item2 = tupledArg.Item2;
		Tuple<int, int> objB = new Tuple<int, int>(item, item2);
		return object.ReferenceEquals(this.outer, objB);
	}
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions