Replace formals generic types in self with resolved values in mtype

If cleanup_virtual is true, then virtual types are also replaced with their bounds.

This function returns self if need_anchor is false.

Example 1

class G[E] end
class H[F] super G[F] end
class X[Z] end
  • Array[E].resolve_for(H[Int]) #-> Array[Int]
  • Array[E].resolve_for(G[Z], X[Int]) #-> Array[Z]

Explanation of the example:

  • Array[E].need_anchor is true because there is a formal generic parameter type E
  • E makes sense for H[Int] because E is a formal parameter of G and H specialize G
  • Since "H[F] super G[F]", E is in fact F for H
  • More specifically, in H[Int], E is Int
  • So, in H[Int], Array[E] is Array[Int]

This function is mainly used to inherit a signature. Because, unlike anchor_to, we do not want a full resolution of a type but only an adapted version of it.

Example 2

class A[E]
    fun foo(e:E):E is abstract
end
class B super A[Int] end

The signature on foo is (e: E): E If we resolve the signature for B, we get (e:Int):Int

Example 3

class A[E]
    fun foo(e:E):E is abstract
end
class C[F]
    var a: A[Array[F]]
    fun bar do a.foo(x) # <- x is here
end

The first question is: is foo available on a?

The static type of a is A[Array[F]], that is an open type. in order to find a method foo, whe must look at a resolved type.

A[Array[F]].anchor_to(C[nullable Object]) #-> A[Array[nullable Object]]

the method foo exists in A[Array[nullable Object]], therefore foo exists for a.

The next question is: what is the accepted types for x?

the signature of foo is foo(e:E), thus we must resolve the type E

E.resolve_for(A[Array[F]],C[nullable Object]) #-> Array[F]

The resolution can be done because E make sense for the class A (see can_resolve_for)

FIXME: the parameter cleanup_virtual is just a bad idea, but having

two function instead of one seems also to be a bad idea.

REQUIRE: can_resolve_for(mtype, anchor, mmodule)

ENSURE: not self.need_anchor implies result == self

Property definitions

nitc $ MType :: resolve_for
	# Replace formals generic types in self with resolved values in `mtype`
	# If `cleanup_virtual` is true, then virtual types are also replaced
	# with their bounds.
	#
	# This function returns self if `need_anchor` is false.
	#
	# ## Example 1
	#
	# ~~~
	# class G[E] end
	# class H[F] super G[F] end
	# class X[Z] end
	# ~~~
	#
	#  * Array[E].resolve_for(H[Int])  #->  Array[Int]
	#  * Array[E].resolve_for(G[Z], X[Int]) #->  Array[Z]
	#
	# Explanation of the example:
	#  * Array[E].need_anchor is true because there is a formal generic parameter type E
	#  * E makes sense for H[Int] because E is a formal parameter of G and H specialize G
	#  * Since "H[F] super G[F]", E is in fact F for H
	#  * More specifically, in H[Int], E is Int
	#  * So, in H[Int], Array[E] is Array[Int]
	#
	# This function is mainly used to inherit a signature.
	# Because, unlike `anchor_to`, we do not want a full resolution of
	# a type but only an adapted version of it.
	#
	# ## Example 2
	#
	# ~~~
	# class A[E]
	#     fun foo(e:E):E is abstract
	# end
	# class B super A[Int] end
	# ~~~
	#
	# The signature on foo is (e: E): E
	# If we resolve the signature for B, we get (e:Int):Int
	#
	# ## Example 3
	#
	# ~~~nitish
	# class A[E]
	#     fun foo(e:E):E is abstract
	# end
	# class C[F]
	#     var a: A[Array[F]]
	#     fun bar do a.foo(x) # <- x is here
	# end
	# ~~~
	#
	# The first question is: is foo available on `a`?
	#
	# The static type of a is `A[Array[F]]`, that is an open type.
	# in order to find a method `foo`, whe must look at a resolved type.
	#
	#   A[Array[F]].anchor_to(C[nullable Object])  #->  A[Array[nullable Object]]
	#
	# the method `foo` exists in `A[Array[nullable Object]]`, therefore `foo` exists for `a`.
	#
	# The next question is: what is the accepted types for `x`?
	#
	# the signature of `foo` is `foo(e:E)`, thus we must resolve the type E
	#
	#   E.resolve_for(A[Array[F]],C[nullable Object])  #->  Array[F]
	#
	# The resolution can be done because `E` make sense for the class A (see `can_resolve_for`)
	#
	# FIXME: the parameter `cleanup_virtual` is just a bad idea, but having
	# two function instead of one seems also to be a bad idea.
	#
	# REQUIRE: `can_resolve_for(mtype, anchor, mmodule)`
	# ENSURE: `not self.need_anchor implies result == self`
	fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MType is abstract
src/model/model.nit:1050,2--1124,119

nitc $ MClassType :: resolve_for
	redef fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MClassType do return self
src/model/model.nit:1318,2--133

nitc $ MProxyType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
	do
		var res = self.mtype.resolve_for(mtype, anchor, mmodule, cleanup_virtual)
		return res
	end
src/model/model.nit:1786,2--1790,4

nitc $ MNullType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
src/model/model.nit:1903,2--78

nitc $ MBottomType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
src/model/model.nit:1929,2--78

nitc $ MErrorType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
src/model/model.nit:1953,2--78

nitc $ MSignature :: resolve_for
	redef fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MSignature
	do
		var params = new Array[MParameter]
		for p in self.mparameters do
			params.add(p.resolve_for(mtype, anchor, mmodule, cleanup_virtual))
		end
		var ret = self.return_mtype
		if ret != null then
			ret = ret.resolve_for(mtype, anchor, mmodule, cleanup_virtual)
		end
		var res = new MSignature(params, ret)
		return res
	end
src/model/model.nit:2081,2--2093,4

nitc $ MInitType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
src/vm/virtual_machine.nit:900,2--78

nitc $ MRawType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do
		not_available
		return self
	end
src/doc/model_ext.nit:40,2--43,4

nitc $ MGenericType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
	do
		if not need_anchor then return self
		assert can_resolve_for(mtype, anchor, mmodule)
		var types = new Array[MType]
		for t in arguments do
			types.add(t.resolve_for(mtype, anchor, mmodule, cleanup_virtual))
		end
		return mclass.get_mtype(types)
	end
src/model/model.nit:1446,2--1455,4

nitc $ MVirtualType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
	do
		if not cleanup_virtual then return self
		assert can_resolve_for(mtype, anchor, mmodule)

		if mproperty.is_selftype then return mtype

		# self is a virtual type declared (or inherited) in mtype
		# The point of the function it to get the bound of the virtual type that make sense for mtype
		# But because mtype is maybe a virtual/formal type, we need to get a real receiver first
		#print "{class_name}: {self}/{mtype}/{anchor}?"
		var resolved_receiver
		if mtype.need_anchor then
			assert anchor != null
			resolved_receiver = mtype.resolve_for(anchor, null, mmodule, true)
		else
			resolved_receiver = mtype
		end
		# Now, we can get the bound
		var verbatim_bound = lookup_bound(mmodule, resolved_receiver)
		# The bound is exactly as declared in the "type" property, so we must resolve it again
		var res = verbatim_bound.resolve_for(mtype, anchor, mmodule, cleanup_virtual)

		return res
	end
src/model/model.nit:1581,2--1605,4

nitc $ MParameterType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
	do
		assert can_resolve_for(mtype, anchor, mmodule)
		#print "{class_name}: {self}/{mtype}/{anchor}?"

		if mtype isa MGenericType and mtype.mclass == self.mclass then
			return mtype.arguments[self.rank]
		end

		# self is a parameter type of mtype (or of a super-class of mtype)
		# The point of the function it to get the bound of the virtual type that make sense for mtype
		# But because mtype is maybe a virtual/formal type, we need to get a real receiver first
		# FIXME: What happens here is far from clear. Thus this part must be validated and clarified
		var resolved_receiver
		if mtype.need_anchor then
			assert anchor != null
			resolved_receiver = mtype.resolve_for(anchor.mclass.mclass_type, anchor, mmodule, true)
		else
			resolved_receiver = mtype
		end
		if resolved_receiver isa MNullableType then resolved_receiver = resolved_receiver.mtype
		if resolved_receiver isa MParameterType then
			assert anchor != null
			assert resolved_receiver.mclass == anchor.mclass
			resolved_receiver = anchor.arguments[resolved_receiver.rank]
			if resolved_receiver isa MNullableType then resolved_receiver = resolved_receiver.mtype
		end
		assert resolved_receiver isa MClassType # It is the only remaining type

		# Eh! The parameter is in the current class.
		# So we return the corresponding argument, no mater what!
		if resolved_receiver.mclass == self.mclass then
			var res = resolved_receiver.arguments[self.rank]
			#print "{class_name}: {self}/{mtype}/{anchor} -> direct {res}"
			return res
		end

		if resolved_receiver.need_anchor then
			assert anchor != null
			resolved_receiver = resolved_receiver.resolve_for(anchor, null, mmodule, false)
		end
		# Now, we can get the bound
		var verbatim_bound = lookup_bound(mmodule, resolved_receiver)
		# The bound is exactly as declared in the "type" property, so we must resolve it again
		var res = verbatim_bound.resolve_for(mtype, anchor, mmodule, cleanup_virtual)

		#print "{class_name}: {self}/{mtype}/{anchor} -> indirect {res}"

		return res
	end
src/model/model.nit:1709,2--1758,4

nitc $ MNullableType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
	do
		var res = super
		return res.as_nullable
	end
src/model/model.nit:1846,2--1850,4

nitc $ MNotNullType :: resolve_for
	redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
	do
		var res = super
		return res.as_notnull
	end
src/model/model.nit:1875,2--1879,4