mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Use resolved names in typechecker (#974)
As a byproduct, replace NamedEntity with a type-erasing wrapper NamedEntityView, eliminate virtual inheritance from the AST, and eliminate interfaces from gen_rtti.
This commit is contained in:
@@ -2,42 +2,44 @@
|
||||
|
||||
"""Generates C++ header to support LLVM-style RTTI for a class hierarchy.
|
||||
|
||||
Takes as input a file describing the class hierarchy which can consist of
|
||||
four different kinds of classes: a *root* class is the base of a class
|
||||
hierarchy, meaning that it doesn't inherit from any other class. *Abstract* and
|
||||
*interface* classes are non-root classes that cannot be instantiated, and
|
||||
*concrete* classes are classes that can be instantiated.
|
||||
# Background
|
||||
|
||||
A non-root class C must inherit from exactly one parent, which can be a root or
|
||||
abstract class, and can also inherit from any number of interfaces, but each
|
||||
interface's parent must be an ancestor of C.
|
||||
A C++ class hierarchy supported by this script consists of *abstract* classes,
|
||||
which can be inherited from but can't be instantiated, and *concrete* classes,
|
||||
which can be instantiated but can't be inherited from. Classes can inherit from
|
||||
at most one other class in the hierarchy; a class that doesn't inherit from
|
||||
any other class is called a *root* class, and it cannot be concrete.
|
||||
|
||||
# Input format
|
||||
|
||||
This script's input file declares every class in the hierarchy, and specifies
|
||||
the parent of each non-root class. The input file consists of comment lines
|
||||
starting with `#`, whitespace lines, and one `;`-terminated line for each class.
|
||||
The core of a line is `class` followed by the class name. `class` can be
|
||||
prefixed with `root` or `abstract` to specify the corresponding kind of class;
|
||||
if there is no prefix, the class is concrete. If the class is not a root class,
|
||||
the name is followed by `:` and then the name of the class it inherits from. A
|
||||
class cannot inherit from a class defined later in the file.
|
||||
|
||||
The input file consists of comment lines starting with `#`, whitespace lines,
|
||||
and one `;`-terminated line for each class. The core of a line is `class`
|
||||
followed by the class name. `class` can be prefixed with `root`, `abstract`,
|
||||
or `interface` to specify the corresponding kind of class; if there is no
|
||||
prefix, the class is concrete. If the class is not a root class, the name is
|
||||
followed by `:` and then a comma-separated list of the names of the classes
|
||||
it inherits from. The first entry in the list is the parent, and the others
|
||||
are interfaces. A class cannot inherit from classes defined later in the file.
|
||||
For example:
|
||||
|
||||
root class R;
|
||||
abstract class A : R;
|
||||
interface class I : R;
|
||||
abstract class B : R, I;
|
||||
abstract class B : R;
|
||||
class C : A;
|
||||
class D : B;
|
||||
class E : A, I;
|
||||
class E : A;
|
||||
|
||||
For each non-concrete class `Foo`, the generated header file will contain
|
||||
# Output
|
||||
|
||||
For each abstract class `Foo`, the generated header file will contain
|
||||
`enum class FooKind`, which has an enumerator for each concrete class derived
|
||||
from `Foo`, with a name that matches the concrete class name.
|
||||
|
||||
For each non-root class `Foo` whose root class is `Root`, the generated header
|
||||
file will also contain a function `bool InheritsFromFoo(RootKind kind)`,
|
||||
which returns true if the value of `kind` corresponds to a class that is
|
||||
derived from `Foo`. This function can be used to implement `Foo::classof`.
|
||||
file will also contain a function `bool InheritsFromFoo(RootKind kind)`, which
|
||||
returns true if the value of `kind` corresponds to a class that is derived from
|
||||
`Foo`. This function can be used to implement `Foo::classof`.
|
||||
|
||||
All enumerators that represent the same concrete class will have the same
|
||||
numeric value, so you can use `static_cast` to convert between the enum types
|
||||
@@ -64,11 +66,9 @@ class Class:
|
||||
|
||||
Attributes set at construction:
|
||||
name: The class name.
|
||||
kind: The class kind (root, abstract, interface, or concrete)
|
||||
kind: The class kind (root, abstract, or concrete)
|
||||
ancestors: A list of Class objects representing the class's ancestors,
|
||||
starting with the root and ending with the current class's parent.
|
||||
interfaces: A list of Class objects representing the interfaces the class
|
||||
inherits from.
|
||||
_children: A list of Class objects representing the classes that are
|
||||
derived directly from this one.
|
||||
|
||||
@@ -78,18 +78,15 @@ class Class:
|
||||
id_range (ROOT and ABSTRACT only): A pair such that a Class
|
||||
object `c` represents a concrete class derived from `self` if and only
|
||||
if c.id >= self.id_range[0] and c.id < self.id_range[1].
|
||||
leaf_ids (INTERFACE only): A set containing the IDs of all concrete
|
||||
classes derived from this interface.
|
||||
leaves (ROOT only): A list of all concrete classes derived from this one,
|
||||
indexed by their IDs.
|
||||
"""
|
||||
|
||||
Kind = enum.Enum("Kind", "ROOT ABSTRACT INTERFACE CONCRETE")
|
||||
Kind = enum.Enum("Kind", "ROOT ABSTRACT CONCRETE")
|
||||
|
||||
def __init__(self, name, kind, parent, interfaces):
|
||||
def __init__(self, name, kind, parent):
|
||||
self.name = name
|
||||
self.kind = kind
|
||||
self.interfaces = interfaces
|
||||
|
||||
assert (parent is None) == (kind == Class.Kind.ROOT)
|
||||
if parent is None:
|
||||
@@ -102,8 +99,6 @@ class Class:
|
||||
self.id_range = None
|
||||
elif self.kind == Class.Kind.ABSTRACT:
|
||||
self.id_range = None
|
||||
elif self.kind == Class.Kind.INTERFACE:
|
||||
self.leaf_ids = set()
|
||||
else:
|
||||
self.id = None
|
||||
|
||||
@@ -113,9 +108,6 @@ class Class:
|
||||
if parent:
|
||||
parent._children.append(self)
|
||||
|
||||
for interface in self.interfaces:
|
||||
interface._children.append(self)
|
||||
|
||||
def Parent(self):
|
||||
"""Returns this Class's parent."""
|
||||
return self.ancestors[-1]
|
||||
@@ -130,9 +122,9 @@ class Class:
|
||||
def _RegisterLeaf(self, leaf):
|
||||
"""Records that `leaf` is derived from self.
|
||||
|
||||
Also recursively updates the parent and interfaces of self. leaf.id must
|
||||
already be populated, and leaves must be registered in order of ID. This
|
||||
operation is idempotent."""
|
||||
Also recursively updates the parent of self. leaf.id must already be
|
||||
populated, and leaves must be registered in order of ID. This operation
|
||||
is idempotent."""
|
||||
already_visited = False
|
||||
if self.kind == Class.Kind.ROOT:
|
||||
if leaf.id == len(self.leaves):
|
||||
@@ -155,17 +147,9 @@ class Class:
|
||||
assert self.id_range[1] == leaf.id + 1
|
||||
already_visited = True
|
||||
|
||||
elif self.kind == Class.Kind.INTERFACE:
|
||||
if leaf.id in self.leaf_ids:
|
||||
already_visited = True
|
||||
else:
|
||||
self.leaf_ids.add(leaf.id)
|
||||
|
||||
if not already_visited:
|
||||
if self.kind != Class.Kind.ROOT:
|
||||
self.Parent()._RegisterLeaf(leaf)
|
||||
for interface in self.interfaces:
|
||||
interface._RegisterLeaf(leaf)
|
||||
|
||||
def Finalize(self):
|
||||
"""Populates additional attributes for `self` and derived Classes.
|
||||
@@ -176,7 +160,7 @@ class Class:
|
||||
if self.kind == Class.Kind.CONCRETE:
|
||||
self.id = len(self.Root().leaves)
|
||||
self._RegisterLeaf(self)
|
||||
elif self.kind in [Class.Kind.ROOT, Class.Kind.ABSTRACT]:
|
||||
else:
|
||||
for child in self._children:
|
||||
child.Finalize()
|
||||
|
||||
@@ -185,7 +169,6 @@ _LINE_PATTERN = r"""(?P<prefix> \w*) \s*
|
||||
class \s+
|
||||
(?P<name> \w+)
|
||||
(?: \s*:\s* (?P<parent> \w+)
|
||||
(?: , (?P<interfaces> .*) )?
|
||||
)?
|
||||
;$"""
|
||||
|
||||
@@ -210,8 +193,6 @@ def main():
|
||||
kind = Class.Kind.ROOT
|
||||
elif prefix == "abstract":
|
||||
kind = Class.Kind.ABSTRACT
|
||||
elif prefix == "interface":
|
||||
kind = Class.Kind.INTERFACE
|
||||
else:
|
||||
sys.exit(f"Unrecognized class prefix '{prefix}' on line {line_num}")
|
||||
|
||||
@@ -225,36 +206,14 @@ def main():
|
||||
sys.exit(f"Unknown class '{parent_name}' on line {line_num}")
|
||||
if parent.kind == Class.Kind.CONCRETE:
|
||||
sys.exit(f"{parent.name} cannot be a parent on line {line_num}")
|
||||
elif parent.kind == Class.Kind.INTERFACE:
|
||||
if kind != Class.Kind.INTERFACE:
|
||||
sys.exit(
|
||||
"Interface cannot be parent of non-interface on"
|
||||
+ f" line {line_num}"
|
||||
)
|
||||
else:
|
||||
if kind != Class.Kind.ROOT:
|
||||
sys.exit(
|
||||
f"Non-root class must have a parent on line {line_num}"
|
||||
)
|
||||
|
||||
interfaces = []
|
||||
if match_result.group("interfaces"):
|
||||
for unstripped_name in match_result.group("interfaces").split(","):
|
||||
interface_name = unstripped_name.strip()
|
||||
interface = classes[interface_name]
|
||||
if not interface:
|
||||
sys.exit(
|
||||
f"Unknown class '{interface_name}' on line {line_num}"
|
||||
)
|
||||
if interface.kind != Class.Kind.INTERFACE:
|
||||
sys.exit(
|
||||
f"'{interface_name}' used as interface on"
|
||||
+ f" line {line_num}"
|
||||
)
|
||||
interfaces.append(interface)
|
||||
|
||||
classes[match_result.group("name")] = Class(
|
||||
match_result.group("name"), kind, parent, interfaces
|
||||
match_result.group("name"), kind, parent
|
||||
)
|
||||
|
||||
for node in classes.values():
|
||||
@@ -275,16 +234,13 @@ def main():
|
||||
|
||||
for node in classes.values():
|
||||
if node.kind != Class.Kind.CONCRETE:
|
||||
if node.kind == Class.Kind.INTERFACE:
|
||||
ids = sorted(node.leaf_ids)
|
||||
else:
|
||||
ids = range(node.id_range[0], node.id_range[1])
|
||||
ids = range(node.id_range[0], node.id_range[1])
|
||||
print(f"enum class {node.name}Kind {{")
|
||||
for id in ids:
|
||||
print(f" {node.Root().leaves[id].name} = {id},")
|
||||
print("};\n")
|
||||
|
||||
if node.kind != Class.Kind.ROOT:
|
||||
if node.kind in [Class.Kind.ABSTRACT, Class.Kind.CONCRETE]:
|
||||
print(
|
||||
f"inline bool InheritsFrom{node.name}({node.Root().name}Kind"
|
||||
+ " kind) {"
|
||||
@@ -305,19 +261,6 @@ def main():
|
||||
+ f"::{range_end}"
|
||||
)
|
||||
print(" ;")
|
||||
elif node.kind == Class.Kind.INTERFACE:
|
||||
print(" switch(kind) {")
|
||||
is_empty = True
|
||||
for id in sorted(node.leaf_ids):
|
||||
print(
|
||||
f" case {node.Root().name}Kind::"
|
||||
+ f"{node.Root().leaves[id].name}:"
|
||||
)
|
||||
is_empty = False
|
||||
if not is_empty:
|
||||
print(" return true;")
|
||||
print(" default:")
|
||||
print(" return false;\n }")
|
||||
elif node.kind == Class.Kind.CONCRETE:
|
||||
print(
|
||||
f" return kind == {node.Root().name}Kind::{node.name};"
|
||||
|
||||
Reference in New Issue
Block a user