     {:ok, {Sample, [{:abstract_code, {:raw_abstract_v1, forms}}]}} =
       :beam_lib.chunks(@sample_binary, [:abstract_code])
 
-    for {:attribute, _line, :spec, {{:__protocol__, 1}, funspecs}} <- forms,
-        {:type, _line, :fun, [{:type, _, :product, [{:atom, _, clause}]}, return_type]} <- funspecs do
+    for {:attribute, _line, :spec, {{:__protocol__, 1}, specs}} <- forms,
+        {:type, _line, :fun, [{:type, _, :product, [{:atom, _, clause}]}, return_type]} <- specs do
       # Only check that :consolidated? and :impls types changed after consolidation.
       # This prevents underspec warnings in dialyzer on consolidated protocols.
       case clause do
         :consolidated? ->
           assert {:atom, _, true} = return_type
+
         :impls ->
           {:type, _, :tuple, tuple_args} = return_type
+
           assert [
                    {:atom, _, :consolidated},
                    {:type, _, :list, [{:type, _, :union, [{:atom, _, ImplStruct}]}]}
                  ] = tuple_args
+
         _ ->
           :ok
       end

  end

  test "consolidation prevents new implementations" do
    assert ExUnit.CaptureIO.capture_io(:stderr, fn ->
      defimpl WithAny, for: Integer do
        def ok(_any), do: :ok
      end
    end) =~ ~r"the .+WithAny protocol has already been consolidated"
  after
    :code.purge(WithAny.Atom)
    :code.delete(WithAny.Atom)
  end

  test "consolidated implementations without any" do
    assert is_nil Sample.impl_for(:foo)
    assert is_nil Sample.impl_for(fn(x) -> x end)
    assert is_nil Sample.impl_for(1)
    assert is_nil Sample.impl_for(1.1)
    assert is_nil Sample.impl_for([])
    assert is_nil Sample.impl_for([1, 2, 3])
    assert is_nil Sample.impl_for({})
    assert is_nil Sample.impl_for({1, 2, 3})
    assert is_nil Sample.impl_for("foo")
    assert is_nil Sample.impl_for(<<1>>)
    assert is_nil Sample.impl_for(self())
    assert is_nil Sample.impl_for(%{})
    assert is_nil Sample.impl_for(hd(:erlang.ports))
    assert is_nil Sample.impl_for(make_ref())

    assert Sample.impl_for(%ImplStruct{}) ==
           Sample.Protocol.ConsolidationTest.ImplStruct
    assert Sample.impl_for(%NoImplStruct{}) ==
           nil
  end

  test "consolidated implementations with any and tuple fallback" do
    assert WithAny.impl_for(%NoImplStruct{}) == WithAny.Any
    assert WithAny.impl_for(%ImplStruct{}) == WithAny.Any # Derived
    assert WithAny.impl_for(%{__struct__: "foo"}) == WithAny.Map
    assert WithAny.impl_for(%{}) == WithAny.Map
    assert WithAny.impl_for(self()) == WithAny.Any

  end

  test "protocol implementations without any" do
    assert is_nil(Sample.impl_for(:foo))
    assert is_nil(Sample.impl_for(fn x -> x end))
    assert is_nil(Sample.impl_for(1))
    assert is_nil(Sample.impl_for(1.1))
    assert is_nil(Sample.impl_for([]))
    assert is_nil(Sample.impl_for([1, 2, 3]))
    assert is_nil(Sample.impl_for({}))
    assert is_nil(Sample.impl_for({1, 2, 3}))
    assert is_nil(Sample.impl_for("foo"))
    assert is_nil(Sample.impl_for(<<1>>))
    assert is_nil(Sample.impl_for(%{}))
    assert is_nil(Sample.impl_for(self()))
    assert is_nil(Sample.impl_for(hd(:erlang.ports())))
    assert is_nil(Sample.impl_for(make_ref()))

    assert Sample.impl_for(%ImplStruct{}) == Sample.ProtocolTest.ImplStruct
    assert Sample.impl_for(%NoImplStruct{}) == nil
  end

  test "protocol implementation with any and structs fallback" do
    assert WithAny.impl_for(%NoImplStruct{}) == WithAny.Any
    # Derived
    assert WithAny.impl_for(%ImplStruct{}) == WithAny.Any
    assert WithAny.impl_for(%{__struct__: "foo"}) == WithAny.Map
    assert WithAny.impl_for(%{}) == WithAny.Map
    assert WithAny.impl_for(self()) == WithAny.Any
  end

  test "protocol not implemented" do
    message = "protocol ProtocolTest.Sample not implemented for :foo"

    assert_raise Protocol.UndefinedError, message, fn ->
      Sample.ok(:foo)
    end
  end
