Skip to content

Commit 7290159

Browse files
committed
[mypyc] Make namegen not need to use suffixes to disambiguate
Accomplish this by using a mapping that doesn't collide on valid python identifiers. We map '.' to '___' and the (rare and unlikely, but possible) '___' to '_3___'. (This collides '___' with '.3', which is fine.) This makes naming not reliant on the order of processing things which is important for separate compilation, since it will turn out that most of our "internal" names actually do need to be exported (but we would still like to shorten the module name part). I also like the output better, even though it is a little more verbose, since it makes it obvious what is a module seperator and what is an underscore.
1 parent c0b0cc3 commit 7290159

File tree

3 files changed

+32
-46
lines changed

3 files changed

+32
-46
lines changed

mypyc/namegen.py

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,10 @@ class NameGenerator:
2525
respectively. If the modules are 'bar.foo' and 'baz.foo', the
2626
prefixes will be 'bar_foo_' and 'baz_foo_'.
2727
28-
* Replace '.' in the Python name with '_' in the C name. This can
29-
obviously generate conflicts at C name level. If multiple Python
30-
names would map to the same C name using the basic algorithm,
31-
add suffixes _2, _3, etc. to make the C names unique.
32-
33-
* Keep a dictionary of mappings so that we can translate each name
34-
consistently within a build.
28+
* Replace '.' in the Python name with '___' in the C name. (And
29+
replace the unlikely but possible '___' with '___3_'. This
30+
collides '___' with '.3_', but this is OK because names
31+
may not start with a digit.)
3532
3633
The generated should be internal to a build and thus the mapping is
3734
arbitrary. Just generating names '1', '2', ... would be correct,
@@ -66,31 +63,19 @@ def private_name(self, module: str, partial_name: Optional[str] = None) -> str:
6663
"""
6764
# TODO: Support unicode
6865
if partial_name is None:
69-
return self.module_map[module].rstrip('_')
66+
return exported_name(self.module_map[module].rstrip('.'))
7067
if (module, partial_name) in self.translations:
7168
return self.translations[module, partial_name]
7269
if module in self.module_map:
7370
module_prefix = self.module_map[module]
7471
elif module:
75-
module_prefix = module.replace('.', '_') + '_'
72+
module_prefix = module + '.'
7673
else:
7774
module_prefix = ''
78-
candidate = '{}{}'.format(module_prefix, partial_name.replace('.', '_'))
79-
actual = self.make_unique(candidate)
75+
actual = exported_name('{}{}'.format(module_prefix, partial_name))
8076
self.translations[module, partial_name] = actual
81-
self.used_names.add(actual)
8277
return actual
8378

84-
def make_unique(self, name: str) -> str:
85-
if name not in self.used_names:
86-
return name
87-
i = 2
88-
while True:
89-
candidate = '{}_{}'.format(name, i)
90-
if candidate not in self.used_names:
91-
return candidate
92-
i += 1
93-
9479

9580
def exported_name(fullname: str) -> str:
9681
"""Return a C name usable for an exported definition.
@@ -100,8 +85,7 @@ def exported_name(fullname: str) -> str:
10085
builds.
10186
"""
10287
# TODO: Support unicode
103-
# TODO: Ensure that there are no conflicts?
104-
return fullname.replace('.', '___')
88+
return fullname.replace('___', '___3_').replace('.', '___')
10589

10690

10791
def make_module_translation_map(names: List[str]) -> Dict[str, str]:
@@ -124,5 +108,5 @@ def candidate_suffixes(fullname: str) -> List[str]:
124108
components = fullname.split('.')
125109
result = ['']
126110
for i in range(len(components)):
127-
result.append('_'.join(components[-i - 1:]) + '_')
111+
result.append('.'.join(components[-i - 1:]) + '.')
128112
return result

mypyc/ops.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,11 +1416,12 @@ def __init__(self,
14161416
else:
14171417
self.bound_sig = FuncSignature(sig.args[1:], sig.ret_type)
14181418

1419+
@property
1420+
def shortname(self) -> str:
1421+
return self.class_name + '.' + self.name if self.class_name else self.name
1422+
14191423
def cname(self, names: NameGenerator) -> str:
1420-
name = self.name
1421-
if self.class_name:
1422-
name += '_' + self.class_name
1423-
return names.private_name(self.module_name, name)
1424+
return names.private_name(self.module_name, self.shortname)
14241425

14251426

14261427
class FuncIR:

mypyc/test/test_namegen.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,34 @@
77

88
class TestNameGen(unittest.TestCase):
99
def test_candidate_suffixes(self) -> None:
10-
assert candidate_suffixes('foo') == ['', 'foo_']
11-
assert candidate_suffixes('foo.bar') == ['', 'bar_', 'foo_bar_']
10+
assert candidate_suffixes('foo') == ['', 'foo.']
11+
assert candidate_suffixes('foo.bar') == ['', 'bar.', 'foo.bar.']
1212

1313
def test_exported_name(self) -> None:
1414
assert exported_name('foo') == 'foo'
1515
assert exported_name('foo.bar') == 'foo___bar'
1616

1717
def test_make_module_translation_map(self) -> None:
1818
assert make_module_translation_map(
19-
['foo', 'bar']) == {'foo': 'foo_', 'bar': 'bar_'}
19+
['foo', 'bar']) == {'foo': 'foo.', 'bar': 'bar.'}
2020
assert make_module_translation_map(
21-
['foo.bar', 'foo.baz']) == {'foo.bar': 'bar_', 'foo.baz': 'baz_'}
21+
['foo.bar', 'foo.baz']) == {'foo.bar': 'bar.', 'foo.baz': 'baz.'}
2222
assert make_module_translation_map(
23-
['zar', 'foo.bar', 'foo.baz']) == {'foo.bar': 'bar_',
24-
'foo.baz': 'baz_',
25-
'zar': 'zar_'}
23+
['zar', 'foo.bar', 'foo.baz']) == {'foo.bar': 'bar.',
24+
'foo.baz': 'baz.',
25+
'zar': 'zar.'}
2626
assert make_module_translation_map(
27-
['foo.bar', 'fu.bar', 'foo.baz']) == {'foo.bar': 'foo_bar_',
28-
'fu.bar': 'fu_bar_',
29-
'foo.baz': 'baz_'}
27+
['foo.bar', 'fu.bar', 'foo.baz']) == {'foo.bar': 'foo.bar.',
28+
'fu.bar': 'fu.bar.',
29+
'foo.baz': 'baz.'}
3030

3131
def test_name_generator(self) -> None:
3232
g = NameGenerator(['foo', 'foo.zar'])
33-
assert g.private_name('foo', 'f') == 'foo_f'
34-
assert g.private_name('foo', 'C.x.y') == 'foo_C_x_y'
35-
assert g.private_name('foo', 'C.x.y') == 'foo_C_x_y'
36-
assert g.private_name('foo.zar', 'C.x.y') == 'zar_C_x_y'
37-
assert g.private_name('foo', 'C.x_y') == 'foo_C_x_y_2'
38-
assert g.private_name('foo', 'C_x_y') == 'foo_C_x_y_3'
39-
assert g.private_name('foo', 'C_x_y') == 'foo_C_x_y_3'
33+
assert g.private_name('foo', 'f') == 'foo___f'
34+
assert g.private_name('foo', 'C.x.y') == 'foo___C___x___y'
35+
assert g.private_name('foo', 'C.x.y') == 'foo___C___x___y'
36+
assert g.private_name('foo.zar', 'C.x.y') == 'zar___C___x___y'
37+
assert g.private_name('foo', 'C.x_y') == 'foo___C___x_y'
38+
assert g.private_name('foo', 'C_x_y') == 'foo___C_x_y'
39+
assert g.private_name('foo', 'C_x_y') == 'foo___C_x_y'
40+
assert g.private_name('foo', '___') == 'foo______3_'

0 commit comments

Comments
 (0)