# HG changeset patch # User Paul Boddie # Date 1284071269 -7200 # Node ID d5d8fa019db3db62729861c95314a97e809cbe8a # Parent fd4d1b019e7d57cd57c7b5f6096f76f662b3343e Switched the attribute usage mechanism to employ contributor nodes, established by branches, which contribute usage information to the assignment nodes that define actual attribute usage for names. diff -r fd4d1b019e7d -r d5d8fa019db3 TO_DO.txt --- a/TO_DO.txt Tue Sep 07 20:09:20 2010 +0200 +++ b/TO_DO.txt Fri Sep 10 00:27:49 2010 +0200 @@ -1,3 +1,6 @@ +Make use of the _attrcombined annotation instead of _attrnames when deducing types and +defining guards. + Support slicing. This is difficult because __getitem__ has to handle integers and slice objects differently. One could either just try and iterate over the argument and then catch the AttributeError for integers, or one could test the instances first. diff -r fd4d1b019e7d -r d5d8fa019db3 micropython/data.py --- a/micropython/data.py Tue Sep 07 20:09:20 2010 +0200 +++ b/micropython/data.py Fri Sep 10 00:27:49 2010 +0200 @@ -100,9 +100,8 @@ # Attribute users, defining names which use attributes. - self.attribute_users = [{}] # stack of assignments - self.user_shelves = [] - self.loop_users = [{}] # stack of loop nodes + self.attribute_users = [{}] # stack of assignments and branches + self.attribute_user_shelves = [] # Scope usage, indicating the origin of names. @@ -377,8 +376,73 @@ usage = set() for user in self.all_attribute_users: - for name, attrnames in user._attrnames.items(): - usage.add(tuple(attrnames)) + + # First, visit the contributors and combine their usage with the + # usage for each user. + + user._attrcombined = combined_usage = self.get_usage_from_contributors(user) + + for name, all_usage in combined_usage.items(): + for attrnames in all_usage: + usage.add(tuple(attrnames)) + + return usage + + def get_usage_from_contributors(self, node): + + """ + Obtain usage information from the given 'node', combined with usage + details from its contributors, returning a dictionary mapping names to + lists of usage possibilities. + """ + + usage = {} + contributor_usage = {} + + # Visit each contributor, gathering usage for each name. + + for contributor in node._attrbranches: + + # Get contributed usage for each contributor. + # This gathers usage for each name such as {(a, b), (c, d)} and + # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}. + + for name, all_usage in self.get_usage_from_contributors(contributor).items(): + if not contributor_usage.has_key(name): + contributor_usage[name] = set() + contributor_usage[name].update(all_usage) + + # Then get the resulting usage. + + contributed_names = contributor_usage.keys() + current_usage = node._attrnames + + for name in contributed_names: + + # Where contributors define names not present at this level, just + # use the contributed usage. + + current_usage_for_name = current_usage.get(name) + + if current_usage_for_name is None: + usage[name] = contributor_usage[name] + + # Otherwise, combine the current usage with the contributed usage. + # Thus, usage of (f, g) combined with {(a, b), (c, d)} would give + # {(f, g, a, b), (f, g, c, d)}. + + else: + usage[name] = set() + for attrnames in contributor_usage[name]: + usage[name].add(tuple(current_usage_for_name.union(attrnames))) + + # Maintain usage not affected by contributors. + # Usage of (f, g) would become {(f, g)}. + + for name in current_usage.keys(): + if name not in contributed_names: + usage[name] = set([tuple(current_usage[name])]) + return usage def use_attribute(self, name, attrname): @@ -456,17 +520,6 @@ self._init_attribute_user_for_name(node, name) - # Propagate any loop usage forward to any redefinition of a name. - # This models the name acquiring the existing usage as it remains active - # upon starting a new iteration. - - loop_users = self.loop_users[-1] - - if loop_users.has_key(name): - for loop_user in loop_users[name]: - node._attrnames[name].update(loop_user._attrnames[name]) - del loop_users[name] - # Remember this user. self.all_attribute_users.add(node) @@ -475,10 +528,15 @@ "Make sure that 'node' is initialised for 'name'." + self._init_attribute_user(node) + node._attrnames[name] = set() + + def _init_attribute_user(self, node): if not hasattr(node, "_attrnames"): node._attrnames = {} - node._attrnames[name] = set() + if not hasattr(node, "_attrbranches"): + node._attrbranches = [] # Branch management methods. @@ -489,10 +547,10 @@ and subsequently converge. """ - self.user_shelves.append([]) + self.attribute_user_shelves.append([]) self.scope_shelves.append([]) - def _new_branch(self, node, loop=0): + def _new_branch(self, node): """ Establish a new control-flow branch, transferring attribute usage to @@ -502,35 +560,30 @@ usage. """ - new_usage = {} + attribute_users = self.attribute_users[-1] + + # Define this node as the active attribute user for all currently + # defined names. + new_users = {} - # Define usage on this node based on current usage. - - for users in self.attribute_users[-1].values(): - for user in users: - for name, usage in user._attrnames.items(): - if not new_usage.has_key(name): - new_usage[name] = set(usage) - else: - new_usage[name] = new_usage[name].union(set(usage)) - - # Set the usage on this node. - - node._attrnames = {} - - for name, usage in new_usage.items(): - node._attrnames[name] = usage + for name in attribute_users.keys(): new_users[name] = [node] - - # Retain a record of active users. - + self._init_attribute_user_for_name(node, name) + + self._init_attribute_user(node) self.attribute_users.append(new_users) - if loop: - loop_users = {} - loop_users.update(new_users) - self.loop_users.append(loop_users) + # Add this user as a contributor to the previously active users. + + all_users = set() + + for users in attribute_users.values(): + all_users.update(users) + + for user in all_users: + self._init_attribute_user(user) + user._attrbranches.append(node) # Retain a record of scope usage. @@ -538,10 +591,6 @@ scope_usage.update(self.scope_usage[-1]) self.scope_usage.append(scope_usage) - # Remember this user. - - self.all_attribute_users.add(node) - def _abandon_branch(self): """ @@ -561,12 +610,12 @@ """ users = self.attribute_users.pop() - self.user_shelves[-1].append(users) + self.attribute_user_shelves[-1].append(users) scope_usage = self.scope_usage.pop() self.scope_shelves[-1].append(scope_usage) - def _merge_branches(self, loop=0): + def _merge_branches(self): """ Merge control-flow branches. This should find the users active within @@ -577,13 +626,10 @@ # Combine the attribute users. This ensures that a list of users # affected by attribute usage is maintained for the current branch. - if loop: - self.loop_users.pop() - users = self.attribute_users[-1] new_users = {} - all_shelved_users = self.user_shelves.pop() + all_shelved_users = self.attribute_user_shelves.pop() all_user_names = set() # Find all the names defined by the branches. @@ -727,6 +773,12 @@ self.old_scope = old_scope self.new_scope = new_scope +class NullBranch: + + "A class representing an attribute user for a non-existent branch." + + pass + # Program data structures. class Attr: diff -r fd4d1b019e7d -r d5d8fa019db3 micropython/inspect.py --- a/micropython/inspect.py Tue Sep 07 20:09:20 2010 +0200 +++ b/micropython/inspect.py Fri Sep 10 00:27:49 2010 +0200 @@ -350,8 +350,8 @@ def new_branchpoint(self): self.get_namespace()._new_branchpoint() - def new_branch(self, node, loop=0): - self.get_namespace()._new_branch(node, loop) + def new_branch(self, node): + self.get_namespace()._new_branch(node) def abandon_branch(self): self.get_namespace()._abandon_branch() @@ -359,8 +359,8 @@ def shelve_branch(self): self.get_namespace()._shelve_branch() - def merge_branches(self, loop=0): - self.get_namespace()._merge_branches(loop) + def merge_branches(self): + self.get_namespace()._merge_branches() def define_attribute_user(self, node): @@ -786,7 +786,7 @@ # Enter the loop. # Propagate attribute usage to branches. - self.new_branch(node, 1) + self.new_branch(node) self.dispatch(node.body) self.shelve_branch() @@ -794,12 +794,12 @@ # Maintain a branch for the else clause. + self.new_branch(node.else_ or NullBranch()) if node.else_ is not None: - self.new_branch(node.else_) self.dispatch(node.else_) - self.shelve_branch() + self.shelve_branch() - self.merge_branches(1) + self.merge_branches() return None def visitFrom(self, node): @@ -871,10 +871,10 @@ # Maintain a branch for the else clause. + self.new_branch(node.else_ or NullBranch()) if node.else_ is not None: - self.new_branch(node.else_) self.dispatch(node.else_) - self.shelve_branch() + self.shelve_branch() self.merge_branches() return None @@ -932,7 +932,7 @@ # Enter the loop. # Propagate attribute usage to branches. - self.new_branch(node, 1) + self.new_branch(node) for if_ in node.ifs: self.dispatch(if_) @@ -940,7 +940,7 @@ self.shelve_branch() self.in_loop = in_loop - self.merge_branches(1) + self.merge_branches() return None visitListCompIf = NOP @@ -1009,10 +1009,10 @@ self.shelve_branch() + self.new_branch(node.else_ or NullBranch()) if node.else_ is not None: - self.new_branch(node.else_) self.dispatch(node.else_) - self.shelve_branch() + self.shelve_branch() self.merge_branches() return None @@ -1033,7 +1033,7 @@ in_loop = self.in_loop self.in_loop = 1 self.dispatch(node.test) - self.new_branch(node, 1) + self.new_branch(node) self.dispatch(node.body) self.shelve_branch() self.in_loop = in_loop @@ -1041,10 +1041,10 @@ # Maintain a branch for the else clause. # NOTE: Consider merging here before the else clause. + self.new_branch(node.else_ or NullBranch()) if node.else_ is not None: - self.new_branch(node.else_) self.dispatch(node.else_) - self.shelve_branch() + self.shelve_branch() self.merge_branches() return None diff -r fd4d1b019e7d -r d5d8fa019db3 tests/attribute_access_type_restriction_loop_accumulation.py --- a/tests/attribute_access_type_restriction_loop_accumulation.py Tue Sep 07 20:09:20 2010 +0200 +++ b/tests/attribute_access_type_restriction_loop_accumulation.py Fri Sep 10 00:27:49 2010 +0200 @@ -29,16 +29,22 @@ def g(self): return 5 + def h(self): + return 6 + def test_loop(obj, obj2): + # obj: f, g (from loop); f, h (outside loop) while obj.f(): + # obj: g (in loop) obj.g() - obj = obj2 # should support e, f, g + obj = obj2 # obj: e, f, h obj.e() - return obj.f() + # (test invoked again) + return obj.h() c = C() d = D() e = E() -result1_0 = test_loop(d, e) +result_6 = test_loop(d, e) # vim: tabstop=4 expandtab shiftwidth=4