def transitive_add(v, end, assignment): visited = set() while v in assignment: if v in visited or v == end: from lamb import meta from lamb.meta import logger logger.error("breaking loop (v: '%s', end: '%s', visited: '%s', assignment: '%s')" % (v, end, visited, assignment)) break visited |= {v} next_v = assignment[v] assignment[v] = end v = next_v return assignment
def transitive_find_end(v, assignment): # TODO: delete the loop checking from these functions once it's solid start_v = v visited = set() last_v = None while isinstance(v, VariableType) and v in assignment: if v in visited: from lamb import meta from lamb.meta import logger logger.error("breaking loop (start_v: '%s', v: '%s', visited: '%s', assignment: '%s')" % (start_v, v, visited, assignment)) break visited |= {v} last_v = v v = assignment[v] return (v, last_v)
def sub_type_vars(self, assignment, trans_closure=False): if self in assignment: x = assignment[self] if trans_closure: visited = {self} while x in assignment: if x in visited: from lamb import meta from lamb.meta import logger logger.error("breaking loop in substitution (x: '%s', visited: '%s', , assignment: '%s')" % (x, visited, assignment)) break visited |= {x} x = assignment[x] return x else: return self
def unify_r(self, t1, t2, assignment): # nearly the same as for strict types: just enforces that if either type is a variable, we call # that type's unify function (since only it knows what to do with variables.) if self.occurs_check(t1, t2): from lamb import meta from lamb.meta import logger logger.error("Failed occurs check: can't unify recursive types %s and %s" % (t1,t2)) return (None, None) if isinstance(t1, VariableType): if isinstance(t2, VariableType): return t2.unify(t1, self.unify_r, assignment) else: return t1.unify(t2, self.unify_r, assignment) elif isinstance(t2, VariableType): return t2.unify(t1, self.unify_r, assignment) else: (result, r_assign) = t1.unify(t2, self.unify_r, assignment) if result is None: return (None, None) return (result, r_assign)
def unify(self, t2, unify_control_fun, assignment): if self == t2: return (self, assignment) # find the principal type in the equivalence class identified by self. May return self if there's a loop. # (Other sorts of loops should be ruled out?) if self in assignment: (start, prev) = transitive_find_end(self, assignment) else: start = self prev = None # find the principal type in the equivalence class identified by t2. if isinstance(t2, VariableType) and t2 in assignment: (t2_principal, t2_prev) = transitive_find_end(t2, assignment) else: t2_principal = t2 t2_prev = None new_principal = start if PolyTypeSystem.occurs_check(new_principal, t2_principal): from lamb import meta from lamb.meta import logger logger.error("Failed occurs check: can't unify recursive types %s and %s" % (new_principal, t2_principal)) return (None, None) if isinstance(start, VariableType): if not isinstance(t2_principal, VariableType): t2_principal = t2_principal.sub_type_vars(assignment, trans_closure=True) assignment = replace_in_assign(start, t2_principal, assignment) if start != t2_principal: assignment[start] = t2_principal new_principal = t2_principal if isinstance(t2_principal, VariableType): if not isinstance(start, VariableType): start = start.sub_type_vars(assignment, trans_closure=True) assignment = replace_in_assign(t2_principal, start, assignment) if t2_principal != start: assignment[t2_principal] = start new_principal = start if not (isinstance(start, VariableType) or isinstance(t2_principal, VariableType)): new_principal, assignment = unify_control_fun(start, t2_principal, assignment) if new_principal is None: return (None, None) return (new_principal, assignment)