Improve remapping of initialised type parameters

This improves the compiler's support for remapping type parameters to
the appropriate type parameter instance. For example, consider type T
that has type parameter A initialised to type parameter B, and B is
initialised to C. Previously, when looking up the instance of A the
compiler would produce B. As of this commit, the compiler would produce
type C instead. This removes the need for type annotations in various
cases, most notably when working with iterators.

Fixes #117
parent 63959332
......@@ -367,9 +367,14 @@ module Inkoc
types.each do |type|
next unless type.generic_type?
next if type.type_parameter_instances.empty?
instances.merge!(type.type_parameter_instances)
next unless type.object?
type.implemented_traits.each do |_, trait|
instances.merge!(trait.type_parameter_instances)
end
end
if instances.empty?
......@@ -380,6 +385,16 @@ module Inkoc
end
end
end
def lookup_type_parameter_instance(param)
instance = super
if instance&.type_parameter? && param != instance
lookup_type_parameter_instance(instance)
else
instance
end
end
end
end
end
......@@ -143,6 +143,29 @@ module Inkoc
def remove_trait_implementation(trait)
implemented_traits.delete(trait.unique_id)
end
def lookup_type_parameter_instance(param)
if (instance = super)
return instance
end
implemented_traits.each do |_, trait|
instance = trait.lookup_type_parameter_instance(param)
next unless instance
if instance.type_parameter?
# Sometimes a trait's parameter A points to type parameter B defined
# in `self`. In this case we want the instance that is mapped to B,
# not B itself.
return lookup_type_parameter_instance(instance)
end
return instance
end
nil
end
end
end
end
......@@ -896,6 +896,44 @@ describe Inkoc::TypeSystem::Block do
expect(new_block.lookup_type_parameter_instance(param)).to eq(int_type)
expect(block.lookup_type_parameter_instance(param)).to be_nil
end
it 'includes the type parameter instances of any implemented traits' do
block = described_class.new
object = Inkoc::TypeSystem::Object.new
trait = state.typedb.new_trait_type('Thing')
param = trait.define_type_parameter('T')
instance = state.typedb.integer_type.new_instance
trait.initialize_type_parameter(param, instance)
object.implement_trait(trait)
new_block = block.with_type_parameter_instances_from([object])
expect(new_block.lookup_type_parameter_instance(param)).to eq(instance)
end
it 'does not overwrite type parameter instances using trait instances' do
block = described_class.new
object = Inkoc::TypeSystem::Object.new
trait = state.typedb.new_trait_type('Thing')
param = trait.define_type_parameter('T')
trait_instance = state.typedb.integer_type.new_instance
object_instance = state.typedb.float_type.new_instance
trait.initialize_type_parameter(param, trait_instance)
object.initialize_type_parameter(param, object_instance)
object.implement_trait(trait)
new_block = block.with_type_parameter_instances_from([object])
expect(new_block.lookup_type_parameter_instance(param))
.to eq(object_instance)
end
end
end
end
......@@ -668,7 +668,6 @@ describe Inkoc::TypeSystem::Object do
expect(type.type_parameter_instances).to be_empty
end
end
describe '#guard_unknown_message?' do
......@@ -722,4 +721,46 @@ describe Inkoc::TypeSystem::Object do
expect(object.initialize_type_parameter?(param)).to eq(true)
end
end
describe '#lookup_type_parameter_instance' do
it 'returns an instance from the object itself' do
object1 = described_class.new
object2 = described_class.new
param = object1.define_type_parameter('T')
object1.initialize_type_parameter(param, object2)
expect(object1.lookup_type_parameter_instance(param)).to eq(object2)
end
it 'returns an instance from an implemented trait' do
trait = state.typedb.new_trait_type('Thing')
param = trait.define_type_parameter('T')
object1 = described_class.new
object2 = described_class.new
trait.initialize_type_parameter(param, object2)
object1.implement_trait(trait)
expect(object1.lookup_type_parameter_instance(param)).to eq(object2)
end
it 'remaps parameters from traits to the appropriate instances' do
trait = state.typedb.new_trait_type('Thing')
trait_param = trait.define_type_parameter('A')
object1 = described_class.new
object_param = object1.define_type_parameter('B')
object2 = described_class.new
trait.initialize_type_parameter(trait_param, object_param)
object1.initialize_type_parameter(object_param, object2)
object1.implement_trait(trait)
expect(object1.lookup_type_parameter_instance(trait_param)).to eq(object2)
end
end
end
......@@ -107,7 +107,7 @@ def list(path: ToPath) !! IOError -> Array!(Path) {
let paths = raw_paths
.iter
.map do (path: String) { Path.new(path) }
.map do (path) { Path.new(path) }
.to_array
# We know for a fact that the old Array won't be used any more at this point,
......
......@@ -235,7 +235,7 @@ trait Iterator!(T) {
}
}
impl Iterator!(In) for Map!(In, Out) {
impl Iterator!(Out) for Map!(In, Out) {
def next? -> Boolean {
@iterator.next?
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment