def clone(self): # Use node factory? (add as a field to node) decl = self.decl # immutable? imports = collections.clone_collection(self.imports) payloads = collections.clone_collection(self.payloads) globalps = collections.clone_collection(self.globalps) localps = collections.clone_collection(self.localps) clone = Module(self.decl, imports, payloads, globalps, localps) return clone
def check_wellformedness_visit(checker, node_): #context_ = checker.get_context() visited = _context_visit_children_and_cache_contexts(checker, node_) contexts = _peek_cached_contexts(visited) subj = get_subject(visited) # string # Here we first do the checks involving just the information collected from # this choice (push_globalchoice clears enabledroles). Information from # parent is carried over to the new context later below. # # Map from role_ name (string) to set of enabling op (Section 4.6.6 -- # initial operators) names (strings) -- values set in globalmessagetransfer enabled = collections.clone_collection(contexts[0].get_enabled_roles()) for c in contexts[1:]: tmp = c.get_enabled_roles() # Section 4.6.6 -- same set of roles occur in each block if set(tmp.keys()) != set(enabled.keys()): util.report_error("Bad choice block: " + ' ' + str(enabled.keys()) + str(tmp.keys())) # + "\n" + util.pretty_print(c.getnode())) for role_, ops in tmp.items(): #if not(ops == None): if role_ != subj: # Should be generalised for all roles that are already enabled, # not just the subject? # No: already taken care of by the fact # that enabled records only the initial enabling ops (added in # globalmessagetransfer), not all operators # So here maybe # better to use "if not enabled"? for op in ops: # Section 4.6.6 -- initial operators in each block for each # role_ are disjoint if op in enabled[role_]: util.report_error("Bad choice operator: " + op) enabled[role_].add(op) return visited
def check_wellformedness_visit(checker, node_): context = checker.get_context() visited = _context_visit_children_and_cache_contexts(checker, node_) contexts = _peek_cached_contexts(visited) # Section 4.6.8 -- potential operators and sig parameters ops = collections.clone_collection(contexts[0].get_operators()) # includes sig parameters for c in contexts[1:]: tmp = c.get_operators() for (src, dest) in tmp.keys(): if (src, dest) in ops.keys(): us = ops[(src, dest)] them = tmp[(src, dest)] # Section 4.6.8 -- potential operators and parameters disjoint # between each block if not us.isdisjoint(them): util.report_error("Bad parallel operator(s): " + \ str(us & them)) ops[(src, dest)] = us | them # set union else: ops[(src, dest)] = tmp[(src, dest)] return visited
def context_visitor_leave(cv, node_): # Still the "original context" on entering the Choice (not yet updated with # any information from visiting block children) # # Perhaps instead clone the parent context (the context before entering this # choice)? clone = cv.get_context().clone() subj = get_subject(node_) # string contexts = _remove_cached_contexts(node_) # Update enabled roles (initial ops) map (Section 4.6.6 -- initial operators) prev = clone.parent.get_enabled_roles() # Get information from parent context (enabledroles was originally cleared # by push_globalchoice to visit this choice) # # Can factor out to sequencing? Maybe not: this is an instance of generally # carrying information over from the parent context, not just sequencing # sitations ##clone._enabled_roles = {} # HACK (1) for role_, ops in prev.items(): for op in ops: # Carry "initial ops" over from parent context clone = clone.enable_role(role_, op) # Update context with information from visiting this choice's blocks enabled = collections.clone_collection(contexts[0].get_enabled_roles()) for c in contexts[1:]: tmp = c.get_enabled_roles() for role_, ops in tmp.items(): if role_ != subj: enabled[role_] |= ops for role_, ops in enabled.items(): # We only want the "intial ops", so if role_ was already enabled before # this choice, we don't need to record the ops we saw here if role_ not in prev.keys(): for op in ops: clone = clone.enable_role(role_, op) # Update potental operators (Section 4.6.8) map # # N.B. "potential operators" for parallel well-formedness; not "enabling # operators" (already treated above) potops = clone.get_operators() for c in contexts: # New potential operators from visiting blocks, add them all to the # existing map for (src, dest), ops in c.get_operators().items(): for op in ops: if (src, dest) not in potops.keys() or op not in potops[(src, dest)]: # need to collect all operators (for parallel check) from # each choice block as a set clone = clone.add_operator(src, dest, op) #Update reclabs? -- no: don't need to do reclabs because any in-scope #contlab should have been declared in an outer context; any contlab declared #inside the choice cannot possibly be carried over here for c in contexts: for lab in c.get_continue_labels(): clone = clone.add_continue_label(lab) # Reachability and (tail) recursion checks -- this needs to be revised to # properly conform to the langref rec_exitable = False # "Possibly exitable": choice has an exit for c in contexts: if c.get_rec_exitable(): rec_exitable = True break clone = clone.set_rec_exitable(rec_exitable) do_exitable = True for c in contexts: if not c.get_do_exitable(): do_exitable = False break clone = clone.set_do_exitable(do_exitable) for c in contexts: for contlab in c.get_continue_labels(): clone = clone.add_continue_label(contlab) scopes = clone.get_current_scopes() new = [] for c in contexts: for scope in c.get_current_scopes() - scopes: if scope in new: util.report_error("Bad scope: " + scope) new.append(scope) clone = clone.add_scope(scope) cv.set_context(clone.pop_globalchoice(node_))
def clone(self): # { # "Static" info compiled on top-level initialisation (creation, module # loading, visibility building) and not modified later sources = collections.clone_collection(self._sources) modules = collections.clone_collection(self._modules) members = collections.clone_collection(self._members) visiblemodules = collections.clone_collection(self._visible_modules) visiblepayloads = collections.clone_collection(self._visible_payloads) visibleglobals = collections.clone_collection(self._visible_globals) visibleLocals = collections.clone_collection(self._visible_locals) # "Dynamic" info that is modified as the AST is traversed parent = self.parent ast = self.ast module = self.module parameters = collections.clone_collection(self._parameters) roles = collections.clone_collection(self._roles) scope = collections.clone_collection(self._scope) scopes = collections.clone_collection(self._scopes) annotations = collections.clone_collection(self._annotations) enabledroles = collections.clone_collection(self._enabled_roles) operators = collections.clone_collection(self._operators) reclabs = collections.clone_collection(self._rec_labs) contlabs = collections.clone_collection(self._cont_labs) rec_exitable = self._rec_exitable cont_exitable = self._cont_exitable dostack = collections.clone_collection(self._do_stack) do_exitable = self._do_exitable rec_unfoldings = self._rec_unfoldings proto_unfoldings = self._proto_unfoldings projections = self._projections clone = Context(self.import_path, self.payload_path, sources, modules, members, visiblemodules, visiblepayloads, visibleglobals, visibleLocals, parent, ast, module, parameters, roles, scope, scopes, annotations, enabledroles, operators, reclabs, contlabs, rec_exitable, cont_exitable, dostack, do_exitable, rec_unfoldings, proto_unfoldings, projections) return clone
def context_visitor_leave(cv, node_): clone = cv.get_context().clone() # Still the originally pushed context for this parallel (each child was # visited with a cloned visitor) contexts = _remove_cached_contexts(node_) # Section 4.6.8 -- potential operators and sig parameters ops = collections.clone_collection(contexts[0].get_operators()) # includes sig parameters for c in contexts[1:]: tmp = c.get_operators() for (src, dest) in tmp.keys(): if (src, dest) in ops.keys(): ops[(src, dest)] |= tmp[(src, dest)] # set union else: ops[(src, dest)] = tmp[(src, dest)] # Need to do this kind of manual child Context merging every time we process # multiple children subcontexts. don't need to do for e.g. recursion (single # child subcontext) # Add all operators and sig parameters seen in children for each src/dest pair for (src, dest) in ops.keys(): for op in ops[(src, dest)]: if not clone.is_operator_seen(src, dest, op): clone = clone.add_operator(src, dest, op) # Add all operators and sig parameters from parent Context for each src/dest # pair prev = clone.parent.get_operators() for (src, dest) in prev.keys(): for op in prev[(src, dest)]: if not (clone.is_operator_seen(src, dest, op)): clone = clone.add_operator(src, dest, op) # TODO: Parallel deadlocks not specified in langref (maybe should not be) seen = set([]) # Only for used here for checking deadlocks for (src, dest) in clone.get_operators().keys(): if (dest, src) in seen: # too coarse grained? #print '[Warning] Potential deadlock: ', src, dest # FIXME: false positive on A -> B; par { B -> A } -- clearing # operators on par entry would fix this, but may break something # else pass seen.add((src, dest)) # Add all enabled role and initial operators (Section 4.6.6) seen in children # for each src/dest pair current = clone.get_enabled_roles() for c in contexts: for role, ops in c.get_enabled_roles().items(): for op in ops: # Need enabling ops from all par blocks #if not(clone.is_role_enabled(role)): clone = clone.enable_role(role, op) # recLabs no modifications for c in contexts: for lab in c.get_continue_labels(): clone = clone.add_continue_label(lab) rec_exitable = True for c in contexts: if not c.get_rec_exitable(): # TODO: currently any non-rec_exitable child makes the whole parallel # non-rec_exitable. However, this should actually be role-sensitive, # e.g. par { rec X A->B. X } and { C->D} C->D. "rec_exitable" will be # replaced by local projection reachability rec_exitable = False break clone = clone.set_rec_exitable(rec_exitable) do_exitable = True for c in contexts: if not c.get_do_exitable(): do_exitable = False break clone = clone.set_do_exitable(do_exitable) for c in contexts: for contLab in c.get_continue_labels(): clone = clone.add_continue_label(contLab) # Duplicated from globalchoice scopes = clone.get_current_scopes() new = [] for c in contexts: for scope in c.get_current_scopes() - scopes: if scope in new: util.report_error("Bad scope: " + scope) new.append(scope) clone = clone.add_scope(scope) cv.set_context(clone.pop_globalparallel(node_))
def context_visitor_leave(cv, node_): clone = cv.get_context().clone() # Still the originally pushed context for this parallel (each child was # visited with a cloned visitor) contexts = _remove_cached_contexts(node_) # Section 4.6.8 -- potential operators and sig parameters ops = collections.clone_collection(contexts[0].get_operators()) # includes sig parameters for c in contexts[1:]: tmp = c.get_operators() for (src, dest) in tmp.keys(): if (src, dest) in ops.keys(): ops[(src, dest)] |= tmp[(src, dest)] # set union else: ops[(src, dest)] = tmp[(src, dest)] # Need to do this kind of manual child Context merging every time we process # multiple children subcontexts. don't need to do for e.g. recursion (single # child subcontext) # Add all operators and sig parameters seen in children for each src/dest pair for (src, dest) in ops.keys(): for op in ops[(src, dest)]: if not clone.is_operator_seen(src, dest, op): clone = clone.add_operator(src, dest, op) # Add all operators and sig parameters from parent Context for each src/dest # pair prev = clone.parent.get_operators() for (src, dest) in prev.keys(): for op in prev[(src, dest)]: if not(clone.is_operator_seen(src, dest, op)): clone = clone.add_operator(src, dest, op) # TODO: Parallel deadlocks not specified in langref (maybe should not be) seen = set([]) # Only for used here for checking deadlocks for (src, dest) in clone.get_operators().keys(): if (dest, src) in seen: # too coarse grained? #print '[Warning] Potential deadlock: ', src, dest # FIXME: false positive on A -> B; par { B -> A } -- clearing # operators on par entry would fix this, but may break something # else pass seen.add((src, dest)) # Add all enabled role and initial operators (Section 4.6.6) seen in children # for each src/dest pair current = clone.get_enabled_roles() for c in contexts: for role, ops in c.get_enabled_roles().items(): for op in ops: # Need enabling ops from all par blocks #if not(clone.is_role_enabled(role)): clone = clone.enable_role(role, op) # recLabs no modifications for c in contexts: for lab in c.get_continue_labels(): clone = clone.add_continue_label(lab) rec_exitable = True for c in contexts: if not c.get_rec_exitable(): # TODO: currently any non-rec_exitable child makes the whole parallel # non-rec_exitable. However, this should actually be role-sensitive, # e.g. par { rec X A->B. X } and { C->D} C->D. "rec_exitable" will be # replaced by local projection reachability rec_exitable = False break clone = clone.set_rec_exitable(rec_exitable) do_exitable = True for c in contexts: if not c.get_do_exitable(): do_exitable = False break clone = clone.set_do_exitable(do_exitable) for c in contexts: for contLab in c.get_continue_labels(): clone = clone.add_continue_label(contLab) # Duplicated from globalchoice scopes = clone.get_current_scopes() new = [] for c in contexts: for scope in c.get_current_scopes() - scopes: if scope in new: util.report_error("Bad scope: " + scope) new.append(scope) clone = clone.add_scope(scope) cv.set_context(clone.pop_globalparallel(node_))