Generate a Nit "is" for two runtime_variables

Property definitions

nitc $ AbstractCompilerVisitor :: equal_test
	#  Generate a Nit "is" for two runtime_variables
	fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
src/compiler/abstract_compiler.nit:1503,2--1504,77

nitc $ SeparateCompilerVisitor :: equal_test
	redef fun equal_test(value1, value2)
	do
		var res = self.new_var(bool_type)
		if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
			var tmp = value1
			value1 = value2
			value2 = tmp
		end
		if value1.mtype.is_c_primitive then
			var t1 = value1.mtype
			assert t1 == value1.mcasttype

			# Fast case: same C type.
			if value2.mtype == t1 then
				# Same exact C primitive representation.
				self.add("{res} = {value1} == {value2};")
				return res
			end

			# Complex case: value2 has a different representation
			# Thus, it should be checked if `value2` is type-compatible with `value1`
			# This compatibility is done statically if possible and dynamically else

			# Conjunction (ands) of dynamic tests according to the static knowledge
			var tests = new Array[String]

			var t2 = value2.mcasttype
			if t2 isa MNullableType then
				# The destination type cannot be null
				tests.add("({value2} != NULL)")
				t2 = t2.mtype
			else if t2 isa MNullType then
				# `value2` is known to be null, thus incompatible with a primitive
				self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
				return res
			end

			if t2 == t1 then
				# Same type but different representation.
			else if t2.is_c_primitive then
				# Type of `value2` is a different primitive type, thus incompatible
				self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
				return res
			else if t1.is_tagged then
				# To be equal, `value2` should also be correctly tagged
				tests.add("({extract_tag(value2)} == {t1.tag_value})")
			else
				# To be equal, `value2` should also be boxed with the same class
				self.require_declaration("class_{t1.c_name}")
				tests.add "({class_info(value2)} == &class_{t1.c_name})"
			end

			# Compare the unboxed `value2` with `value1`
			if tests.not_empty then
				self.add "if ({tests.join(" && ")}) \{"
			end
			self.add "{res} = {self.autobox(value2, t1)} == {value1};"
			if tests.not_empty then
				self.add "\} else {res} = 0;"
			end

			return res
		end
		var maybe_null = true
		var test = new Array[String]
		var t1 = value1.mcasttype
		if t1 isa MNullableType then
			test.add("{value1} != NULL")
			t1 = t1.mtype
		else
			maybe_null = false
		end
		var t2 = value2.mcasttype
		if t2 isa MNullableType then
			test.add("{value2} != NULL")
			t2 = t2.mtype
		else
			maybe_null = false
		end

		var incompatible = false
		var primitive
		if t1.is_c_primitive then
			primitive = t1
			if t1 == t2 then
				# No need to compare class
			else if t2.is_c_primitive then
				incompatible = true
			else if can_be_primitive(value2) then
				if t1.is_tagged then
					self.add("{res} = {value1} == {value2};")
					return res
				end
				if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
					test.add("(!{extract_tag(value2)})")
				end
				test.add("{value1}->class == {value2}->class")
			else
				incompatible = true
			end
		else if t2.is_c_primitive then
			primitive = t2
			if can_be_primitive(value1) then
				if t2.is_tagged then
					self.add("{res} = {value1} == {value2};")
					return res
				end
				if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
					test.add("(!{extract_tag(value1)})")
				end
				test.add("{value1}->class == {value2}->class")
			else
				incompatible = true
			end
		else
			primitive = null
		end

		if incompatible then
			if maybe_null then
				self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
				return res
			else
				self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
				return res
			end
		end
		if primitive != null then
			if primitive.is_tagged then
				self.add("{res} = {value1} == {value2};")
				return res
			end
			test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
		else if can_be_primitive(value1) and can_be_primitive(value2) then
			if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
				test.add("(!{extract_tag(value1)}) && (!{extract_tag(value2)})")
			end
			test.add("{value1}->class == {value2}->class")
			var s = new Array[String]
			for t, v in self.compiler.box_kinds do
				if t.mclass_type.is_tagged then continue
				s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
			end
			if s.is_empty then
				self.add("{res} = {value1} == {value2};")
				return res
			end
			test.add("({s.join(" || ")})")
		else
			self.add("{res} = {value1} == {value2};")
			return res
		end
		self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
		return res
	end
src/compiler/separate_compiler.nit:1985,2--2139,4

nitc $ GlobalCompilerVisitor :: equal_test
	redef fun equal_test(value1, value2)
	do
		var res = self.new_var(bool_type)
		if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
			var tmp = value1
			value1 = value2
			value2 = tmp
		end
		if value1.mtype.is_c_primitive then
			if value2.mtype == value1.mtype then
				self.add("{res} = {value1} == {value2};")
			else if value2.mtype.is_c_primitive then
				self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
			else
				var mtype1 = value1.mtype.as(MClassType)
				self.add("{res} = ({value2} != NULL) && ({value2}->classid == {self.compiler.classid(mtype1)});")
				self.add("if ({res}) \{")
				self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
				self.add("\}")
			end
		else
			var s = new Array[String]
			for t in self.compiler.live_primitive_types do
				if not t.is_subtype(self.compiler.mainmodule, null, value1.mcasttype) then continue
				if not t.is_subtype(self.compiler.mainmodule, null, value2.mcasttype) then continue
				s.add "({value1}->classid == {self.compiler.classid(t)} && ((struct {t.c_name}*){value1})->value == ((struct {t.c_name}*){value2})->value)"
			end

			if self.compiler.mainmodule.model.get_mclasses_by_name("Pointer") != null then
				var pointer_type = self.compiler.mainmodule.pointer_type
				if value1.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) or
					value2.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) then
					s.add "(((struct {pointer_type.c_name}*){value1})->value == ((struct {pointer_type.c_name}*){value2})->value)"
				end
			end

			if s.is_empty then
				self.add("{res} = {value1} == {value2};")
			else
				self.add("{res} = {value1} == {value2} || ({value1} != NULL && {value2} != NULL && {value1}->classid == {value2}->classid && ({s.join(" || ")}));")
			end
		end
		return res
	end
src/compiler/global_compiler.nit:948,2--991,4