Handle common special cases before doing the effective method invocation

This methods handle the == and != methods and the case of the null receiver. Note: a { is open in the generated C, that enclose and protect the effective method invocation. Client must not forget to close the } after them.

The value returned is the result of the common special cases. If not null, client must compile it with the result of their own effective method invocation.

If before_send can shortcut the whole message sending, a dummy if(0){ is generated to cancel the effective method invocation that will follow

TODO: find a better approach

Property definitions

nitc $ SeparateCompilerVisitor :: before_send
	# Handle common special cases before doing the effective method invocation
	# This methods handle the `==` and `!=` methods and the case of the null receiver.
	# Note: a { is open in the generated C, that enclose and protect the effective method invocation.
	# Client must not forget to close the } after them.
	#
	# The value returned is the result of the common special cases.
	# If not null, client must compile it with the result of their own effective method invocation.
	#
	# If `before_send` can shortcut the whole message sending, a dummy `if(0){`
	# is generated to cancel the effective method invocation that will follow
	# TODO: find a better approach
	private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
	do
		var res: nullable RuntimeVariable = null
		var recv = arguments.first
		var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
		if maybe_null(recv) and consider_null then
			self.add("if ({recv} == NULL) \{")
			if mmethod.name == "==" or mmethod.name == "is_same_instance" then
				res = self.new_var(bool_type)
				var arg = arguments[1]
				if arg.mcasttype isa MNullableType then
					self.add("{res} = ({arg} == NULL);")
				else if arg.mcasttype isa MNullType then
					self.add("{res} = 1; /* is null */")
				else
					self.add("{res} = 0; /* {arg.inspect} cannot be null */")
				end
			else if mmethod.name == "!=" then
				res = self.new_var(bool_type)
				var arg = arguments[1]
				if arg.mcasttype isa MNullableType then
					self.add("{res} = ({arg} != NULL);")
				else if arg.mcasttype isa MNullType then
					self.add("{res} = 0; /* is null */")
				else
					self.add("{res} = 1; /* {arg.inspect} cannot be null */")
				end
			else
				self.add_abort("Receiver is null")
			end
			self.add("\} else \{")
		else
			self.add("\{")
		end
		if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then
			# Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants)
			var arg = arguments[1]
			if arg.mcasttype isa MNullType then
				if res == null then res = self.new_var(bool_type)
				if mmethod.name == "!=" then
					self.add("{res} = 1; /* arg is null and recv is not */")
				else # `==` and `is_same_instance`
					self.add("{res} = 0; /* arg is null but recv is not */")
				end
				self.add("\}") # closes the null case
				self.add("if (0) \{") # what follow is useless, CC will drop it
			end
		end
		return res
	end
src/compiler/separate_compiler.nit:1477,2--1537,4