Skip to content

Commit fb556ae

Browse files
authored
[mypyc] Reorganization of headers and declarations (#7634)
This splits the headers we generate into internal headers (containing type declarations) and external headers (containing function and object declarations) and refactors declarations to be much more driven by HeaderDeclaration objects. This will allow us to use HeaderDeclaration information to populate structures for dynamic linking in separate compilation.
1 parent 7bbc7af commit fb556ae

File tree

3 files changed

+106
-51
lines changed

3 files changed

+106
-51
lines changed

mypyc/emit.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,30 @@
2020

2121

2222
class HeaderDeclaration:
23+
"""A representation of a declaration in C.
24+
25+
This is used to generate declarations in header files and
26+
(optionally) definitions in source files.
27+
28+
Attributes:
29+
decl: C source code for the declaration.
30+
defn: Optionally, C source code for a definition.
31+
dependencies: The names of any objects that must be declared prior.
32+
is_type: Whether the declaration is of a C type. (C types will be declared in
33+
external header files and not marked 'extern'.)
34+
"""
35+
2336
def __init__(self,
24-
dependencies: Set[str], decl: List[str], defn: Optional[List[str]],
25-
needs_extern: bool = False) -> None:
26-
self.dependencies = dependencies
27-
self.decl = decl
37+
decl: Union[str, List[str]],
38+
defn: Optional[List[str]] = None,
39+
*,
40+
dependencies: Optional[Set[str]] = None,
41+
is_type: bool = False
42+
) -> None:
43+
self.decl = [decl] if isinstance(decl, str) else decl
2844
self.defn = defn
29-
self.needs_extern = needs_extern
45+
self.dependencies = dependencies or set()
46+
self.is_type = is_type
3047

3148

3249
class EmitterContext:
@@ -220,9 +237,9 @@ def declare_tuple_struct(self, tuple_type: RTuple) -> None:
220237
dependencies.add(typ.struct_name)
221238

222239
self.context.declarations[tuple_type.struct_name] = HeaderDeclaration(
223-
dependencies,
224240
self.tuple_c_declaration(tuple_type),
225-
None,
241+
dependencies=dependencies,
242+
is_type=True,
226243
)
227244

228245
def emit_inc_ref(self, dest: str, rtype: RType) -> None:

mypyc/emitclass.py

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from typing import Optional, List, Tuple, Dict, Callable, Mapping, Set
55
from collections import OrderedDict
66

7-
from mypyc.common import NATIVE_PREFIX, PREFIX, REG_PREFIX
8-
from mypyc.emit import Emitter
7+
from mypyc.common import PREFIX, NATIVE_PREFIX, REG_PREFIX
8+
from mypyc.emit import Emitter, HeaderDeclaration
99
from mypyc.emitfunc import native_function_header, native_getter_name, native_setter_name
1010
from mypyc.emitwrapper import (
1111
generate_dunder_wrapper, generate_hash_wrapper, generate_richcompare_wrapper,
@@ -78,16 +78,20 @@ def generate_slots(cl: ClassIR, table: SlotTable, emitter: Emitter) -> Dict[str,
7878
return fields
7979

8080

81-
def generate_class_type_decl(cl: ClassIR, c_emitter: Emitter, emitter: Emitter) -> None:
82-
c_emitter.emit_line('PyTypeObject *{};'.format(emitter.type_struct_name(cl)))
83-
emitter.emit_line('extern PyTypeObject *{};'.format(emitter.type_struct_name(cl)))
84-
emitter.emit_line()
85-
generate_object_struct(cl, emitter)
86-
emitter.emit_line()
81+
def generate_class_type_decl(cl: ClassIR, c_emitter: Emitter,
82+
external_emitter: Emitter,
83+
emitter: Emitter) -> None:
84+
context = c_emitter.context
85+
name = emitter.type_struct_name(cl)
86+
context.declarations[name] = HeaderDeclaration(
87+
'PyTypeObject *{};'.format(emitter.type_struct_name(cl)))
88+
89+
generate_object_struct(cl, external_emitter)
8790
declare_native_getters_and_setters(cl, emitter)
8891
generate_full = not cl.is_trait and not cl.builtin_base
8992
if generate_full:
90-
emitter.emit_line('{};'.format(native_function_header(cl.ctor, emitter)))
93+
context.declarations[emitter.native_function_name(cl.ctor)] = HeaderDeclaration(
94+
'{};'.format(native_function_header(cl.ctor, emitter)))
9195

9296

9397
def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None:
@@ -241,30 +245,42 @@ def setter_name(cl: ClassIR, attribute: str, names: NameGenerator) -> str:
241245

242246

243247
def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None:
244-
emitter.emit_lines('typedef struct {',
245-
'PyObject_HEAD',
246-
'CPyVTableItem *vtable;')
247248
seen_attrs = set() # type: Set[Tuple[str, RType]]
249+
lines = [] # type: List[str]
250+
lines += ['typedef struct {',
251+
'PyObject_HEAD',
252+
'CPyVTableItem *vtable;']
248253
for base in reversed(cl.base_mro):
249254
if not base.is_trait:
250255
for attr, rtype in base.attributes.items():
251256
if (attr, rtype) not in seen_attrs:
252-
emitter.emit_line('{}{};'.format(emitter.ctype_spaced(rtype),
253-
emitter.attr(attr)))
257+
lines.append('{}{};'.format(emitter.ctype_spaced(rtype),
258+
emitter.attr(attr)))
254259
seen_attrs.add((attr, rtype))
255-
emitter.emit_line('}} {};'.format(cl.struct_name(emitter.names)))
260+
lines.append('}} {};'.format(cl.struct_name(emitter.names)))
261+
lines.append('')
262+
emitter.context.declarations[cl.struct_name(emitter.names)] = HeaderDeclaration(
263+
lines,
264+
is_type=True
265+
)
256266

257267

258268
def declare_native_getters_and_setters(cl: ClassIR,
259269
emitter: Emitter) -> None:
270+
decls = emitter.context.declarations
260271
for attr, rtype in cl.attributes.items():
261-
emitter.emit_line('{}{}({} *self);'.format(emitter.ctype_spaced(rtype),
262-
native_getter_name(cl, attr, emitter.names),
263-
cl.struct_name(emitter.names)))
264-
emitter.emit_line(
272+
getter_name = native_getter_name(cl, attr, emitter.names)
273+
setter_name = native_setter_name(cl, attr, emitter.names)
274+
decls[getter_name] = HeaderDeclaration(
275+
'{}{}({} *self);'.format(emitter.ctype_spaced(rtype),
276+
getter_name,
277+
cl.struct_name(emitter.names))
278+
)
279+
decls[setter_name] = HeaderDeclaration(
265280
'bool {}({} *self, {}value);'.format(native_setter_name(cl, attr, emitter.names),
266281
cl.struct_name(emitter.names),
267-
emitter.ctype_spaced(rtype)))
282+
emitter.ctype_spaced(rtype))
283+
)
268284

269285

270286
def generate_native_getters_and_setters(cl: ClassIR,

mypyc/emitmodule.py

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,11 @@ def compile_modules_to_c(result: BuildResult, module_names: List[str],
8282

8383

8484
def generate_function_declaration(fn: FuncIR, emitter: Emitter) -> None:
85-
emitter.emit_line('{};'.format(native_function_header(fn.decl, emitter)))
85+
emitter.context.declarations[emitter.native_function_name(fn.decl)] = HeaderDeclaration(
86+
'{};'.format(native_function_header(fn.decl, emitter)))
8687
if fn.name != TOP_LEVEL_NAME:
87-
emitter.emit_line('{};'.format(wrapper_function_header(fn, emitter.names)))
88+
emitter.context.declarations[PREFIX + fn.cname(emitter.names)] = HeaderDeclaration(
89+
'{};'.format(wrapper_function_header(fn, emitter.names)))
8890

8991

9092
def encode_as_c_string(s: str) -> Tuple[str, int]:
@@ -127,6 +129,7 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]:
127129

128130
base_emitter = Emitter(self.context)
129131
base_emitter.emit_line('#include "__native.h"')
132+
base_emitter.emit_line('#include "__native_internal.h"')
130133
emitter = base_emitter
131134

132135
for (_, literal), identifier in self.literals.items():
@@ -140,6 +143,7 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]:
140143
if multi_file:
141144
emitter = Emitter(self.context)
142145
emitter.emit_line('#include "__native.h"')
146+
emitter.emit_line('#include "__native_internal.h"')
143147

144148
self.declare_module(module_name, emitter)
145149
self.declare_internal_globals(module_name, emitter)
@@ -166,6 +170,33 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]:
166170
name = ('__native_{}.c'.format(emitter.names.private_name(module_name)))
167171
file_contents.append((name, ''.join(emitter.fragments)))
168172

173+
# The external header file contains type declarations while
174+
# the internal contains declarations of functions and objects
175+
# (which are shared between shared libraries via dynamic
176+
# linking tables and not accessed directly.)
177+
ext_declarations = Emitter(self.context)
178+
ext_declarations.emit_line('#ifndef MYPYC_NATIVE_H')
179+
ext_declarations.emit_line('#define MYPYC_NATIVE_H')
180+
ext_declarations.emit_line('#include <Python.h>')
181+
ext_declarations.emit_line('#include <CPy.h>')
182+
183+
declarations = Emitter(self.context)
184+
declarations.emit_line('#ifndef MYPYC_NATIVE_INTERNAL_H')
185+
declarations.emit_line('#define MYPYC_NATIVE_INTERNAL_H')
186+
declarations.emit_line('#include <Python.h>')
187+
declarations.emit_line('#include <CPy.h>')
188+
declarations.emit_line('#include "__native.h"')
189+
declarations.emit_line()
190+
declarations.emit_line('int CPyGlobalsInit(void);')
191+
declarations.emit_line()
192+
193+
for module_name, module in self.modules:
194+
self.declare_finals(module_name, module.final_names, declarations)
195+
for cl in module.classes:
196+
generate_class_type_decl(cl, emitter, ext_declarations, declarations)
197+
for fn in module.functions:
198+
generate_function_declaration(fn, declarations)
199+
169200
sorted_decls = self.toposort_declarations()
170201

171202
emitter = base_emitter
@@ -176,33 +207,25 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]:
176207

177208
emitter.emit_line()
178209

179-
declarations = Emitter(self.context)
180-
declarations.emit_line('#include <Python.h>')
181-
declarations.emit_line('#include <CPy.h>')
182-
declarations.emit_line()
183-
declarations.emit_line('int CPyGlobalsInit(void);')
184-
declarations.emit_line()
185-
186210
for declaration in sorted_decls:
187-
if declaration.needs_extern:
188-
declarations.emit_lines(
211+
decls = ext_declarations if declaration.is_type else declarations
212+
if not declaration.is_type:
213+
decls.emit_lines(
189214
'extern {}'.format(declaration.decl[0]), *declaration.decl[1:])
190215
emitter.emit_lines(*declaration.decl)
191216
else:
192-
declarations.emit_lines(*declaration.decl)
193-
194-
for module_name, module in self.modules:
195-
self.declare_finals(module_name, module.final_names, declarations)
196-
for cl in module.classes:
197-
generate_class_type_decl(cl, emitter, declarations)
198-
for fn in module.functions:
199-
generate_function_declaration(fn, declarations)
217+
decls.emit_lines(*declaration.decl)
200218

201219
if self.shared_lib_name:
202220
self.generate_shared_lib_init(emitter)
203221

222+
ext_declarations.emit_line('#endif')
223+
declarations.emit_line('#endif')
224+
204225
return file_contents + [('__native.c', ''.join(emitter.fragments)),
205-
('__native.h', ''.join(declarations.fragments))]
226+
('__native_internal.h', ''.join(declarations.fragments)),
227+
('__native.h', ''.join(ext_declarations.fragments)),
228+
]
206229

207230
def generate_shared_lib_init(self, emitter: Emitter) -> None:
208231
"""Generate the init function for a shared library.
@@ -438,17 +461,16 @@ def _toposort_visit(name: str) -> None:
438461
return result
439462

440463
def declare_global(self, type_spaced: str, name: str,
464+
*,
441465
initializer: Optional[str] = None) -> None:
442466
if not initializer:
443467
defn = None
444468
else:
445469
defn = ['{}{} = {};'.format(type_spaced, name, initializer)]
446470
if name not in self.context.declarations:
447471
self.context.declarations[name] = HeaderDeclaration(
448-
set(),
449-
['{}{};'.format(type_spaced, name)],
450-
defn,
451-
needs_extern=True,
472+
'{}{};'.format(type_spaced, name),
473+
defn=defn,
452474
)
453475

454476
def declare_internal_globals(self, module_name: str, emitter: Emitter) -> None:

0 commit comments

Comments
 (0)