def is_var_visible_in_block(self, var: Var, upto_op: Optional[Operation] = None):
        """
        Checks if a var is visible to ops starting from id=`upto_op_with_id` inside the block.

        Var is visible if
        - It is the output of a const op, or
        - It is the output of "preceding" operations in that block, or
        - It is visible in the enclosing block, or
        - It is either a block or a function input

        If upto_op_with_id is None, outputs of all operations inside the block are visible to
        that block.

        For debugging:
        - By default (DEBUG=False), this utils is guarded by the flag in calling code and not running.
        - By setting DEBUG=True, this utils is triggered in multiple places in the code base,
          so the users can pinpoint the exact place where an invalid operation is made by the converter.
          Beware that, the converter could be slow in the debug mode, since the overal conversion
          time will explode to O(N^2) in the average cases by this util.
        """
        if not DEBUG:
            # Only in debug mode, there is a chance that self.operations is type of list when executing this function.
            assert isinstance(
                self.operations, CacheDoublyLinkedList
            ), "operations must be type of CacheDoublyLinkedList."

        if var in self._internal_vars:
            return True

        inputs = list(self.inputs.values()) if isinstance(self, Function) else self.inputs
        if var in inputs:
            return True

        if upto_op is None:
            if var.op in self.operations:
                return True
        else:
            if isinstance(self.operations, list):
                # This could only happen in debug mode
                assert DEBUG is True, "block.operations can only be type of list in debug mode."
                idx = self.find_op_id_in_block(upto_op)
                for i in range(idx - 1, -1, -1):
                    if var.op is self.operations[i]:
                        return True
            else:
                cursor = self.operations._get_node_from_op(upto_op).prev
                while cursor is not None:
                    if cursor.op is var.op:
                        return True
                    cursor = cursor.prev

        if self.outer_op is not None:
            enclosing_block = self.outer_op.enclosing_block
            if enclosing_block.is_var_visible_in_block(var, upto_op=self.outer_op):
                return True

        return False

        Checks if a var is visible to ops starting from id=`upto_op_with_id` inside the block.

        Var is visible if
        - It is the output of a const op, or
        - It is the output of "preceding" operations in that block, or
        - It is visible in the enclosing block, or
        - It is either a block or a function input

        If upto_op_with_id is None, outputs of all operations inside the block are visible to
        that block.

        For debugging:
        - By default (DEBUG=False), this utils is guarded by the flag in calling code and not running.
        - By setting DEBUG=True, this utils is triggered in multiple places in the code base,
          so the users can pinpoint the exact place where an invalid operation is made by the converter.
          Beware that, the converter could be slow in the debug mode, since the overal conversion
          time will explode to O(N^2) in the average cases by this util.
        
coremltools/converters/mil/mil/block.py