Skip to content

Commit d2f2e8a

Browse files
authored
Improve superclass retrieval API (#933)
- Let's call it `get_superclass` instead, to match Ruby's `superclass` API name - `get_superclass` should handle `Object` and `BasicObject`'s superclass correctly - Caller of `get_superclass` should not need to pass definitions separately
1 parent 2b91c78 commit d2f2e8a

2 files changed

Lines changed: 57 additions & 41 deletions

File tree

docs/ruby-behaviors.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,25 @@ Ruby builds ancestors through the following steps recursively:
12671267
12681268
And then it continues up the inheritance chain.
12691269
1270+
A class without an explicit superclass inherits from `Object`. Exceptions are:
1271+
1272+
- `Object` itself inherits from `BasicObject`
1273+
- `BasicObject` does NOT have any superclass.
1274+
1275+
```ruby
1276+
class Foo; end
1277+
1278+
Foo.superclass # => Object
1279+
Object.superclass # => BasicObject
1280+
BasicObject.superclass # => nil
1281+
1282+
Foo.ancestors # => [Foo, Object, Kernel, BasicObject]
1283+
Object.ancestors # => [Object, Kernel, BasicObject]
1284+
BasicObject.ancestors # => [BasicObject]
1285+
```
1286+
1287+
Singleton classes use a separate root rule: `BasicObject.singleton_class.superclass` is `Class`.
1288+
12701289
```rb
12711290
module PrependedInPrepended
12721291
end

rust/rubydex/src/resolution.rs

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,17 +1067,11 @@ impl<'a> Resolver<'a> {
10671067
declaration_id: DeclarationId,
10681068
context: &mut LinearizationContext,
10691069
) -> Option<Vec<Ancestor>> {
1070-
if declaration_id == *BASIC_OBJECT_ID {
1071-
return None;
1072-
}
1073-
10741070
let declaration = self.graph.declarations().get(&declaration_id).unwrap();
10751071

10761072
match declaration {
10771073
Declaration::Namespace(Namespace::Class(_)) => {
1078-
let definition_ids = declaration.definitions().to_vec();
1079-
1080-
Some(match self.linearize_parent_class(&definition_ids, context) {
1074+
Some(match self.linearize_superclass(declaration_id, context)? {
10811075
Ancestors::Complete(ids) => ids,
10821076
Ancestors::Cyclic(ids) => {
10831077
context.cyclic = true;
@@ -2020,11 +2014,6 @@ impl<'a> Resolver<'a> {
20202014
/// - Class: parent is the singleton class of the original parent class
20212015
/// - Singleton class: recurse as many times as necessary to wrap the original attached object's parent class
20222016
fn singleton_parent_id(&mut self, attached_id: DeclarationId) -> (DeclarationId, bool) {
2023-
// Base case: if we reached `BasicObject`, then the parent is `Class`
2024-
if attached_id == *BASIC_OBJECT_ID {
2025-
return (*CLASS_ID, false);
2026-
}
2027-
20282017
let decl = self.graph.declarations().get(&attached_id).unwrap();
20292018

20302019
match decl {
@@ -2042,14 +2031,15 @@ impl<'a> Resolver<'a> {
20422031
)
20432032
}
20442033
Declaration::Namespace(Namespace::Class(_)) => {
2045-
// For classes (the regular case), we need to return the singleton class of its parent
2046-
let definition_ids = decl.definitions().to_vec();
2047-
2048-
let (picked_parent, unresolved_parent) = self.get_parent_class(&definition_ids);
2034+
// For classes (the regular case), we need to return the singleton class of its superclass
2035+
let Some((superclass_id, unresolved_superclass)) = self.get_superclass(attached_id) else {
2036+
// BasicObject has no superclass, but its singleton class inherits from Class
2037+
return (*CLASS_ID, false);
2038+
};
20492039
(
2050-
self.get_or_create_singleton_class(picked_parent, SingletonAncestors::Deferred)
2051-
.expect("parent class should always be a namespace"),
2052-
unresolved_parent.is_some(),
2040+
self.get_or_create_singleton_class(superclass_id, SingletonAncestors::Deferred)
2041+
.expect("superclass should always be a namespace"),
2042+
unresolved_superclass.is_some(),
20532043
)
20542044
}
20552045
_ => {
@@ -2060,11 +2050,18 @@ impl<'a> Resolver<'a> {
20602050
}
20612051
}
20622052

2063-
fn get_parent_class(&self, definition_ids: &[DefinitionId]) -> (DeclarationId, Option<NameId>) {
2064-
let mut explicit_parents = Vec::new();
2065-
let mut unresolved_parent = None;
2053+
/// Returns the selected superclass and any unresolved explicit superclass. The top-level `BasicObject` declaration
2054+
/// is Ruby's root class and is the only class without a superclass.
2055+
fn get_superclass(&self, declaration_id: DeclarationId) -> Option<(DeclarationId, Option<NameId>)> {
2056+
if declaration_id == *BASIC_OBJECT_ID {
2057+
return None;
2058+
}
2059+
2060+
let declaration = self.graph.declarations().get(&declaration_id).unwrap();
2061+
let mut explicit_superclasses = Vec::new();
2062+
let mut unresolved_superclass = None;
20662063

2067-
for definition_id in definition_ids {
2064+
for definition_id in declaration.definitions() {
20682065
let definition = self.graph.definitions().get(definition_id).unwrap();
20692066

20702067
if let Definition::Class(class) = definition
@@ -2075,47 +2072,47 @@ impl<'a> Resolver<'a> {
20752072

20762073
match name {
20772074
NameRef::Resolved(resolved) => {
2078-
if let Some(parent_id) = self.resolve_to_namespace(*resolved.declaration_id()) {
2079-
explicit_parents.push(parent_id);
2075+
if let Some(superclass_id) = self.resolve_to_namespace(*resolved.declaration_id()) {
2076+
explicit_superclasses.push(superclass_id);
20802077
}
20812078
}
20822079
NameRef::Unresolved(_) => {
2083-
unresolved_parent = Some(*constant_reference.name_id());
2080+
unresolved_superclass = Some(*constant_reference.name_id());
20842081
}
20852082
}
20862083
}
20872084
}
20882085

2089-
// If there's more than one parent class that isn't `Object` and they are different, then there's a superclass
2086+
// If there's more than one superclass that isn't `Object` and they are different, then there's a superclass
20902087
// mismatch error. TODO: We should add a diagnostic here
2091-
(
2092-
explicit_parents.first().copied().unwrap_or(*OBJECT_ID),
2093-
unresolved_parent,
2094-
)
2088+
Some((
2089+
explicit_superclasses.first().copied().unwrap_or(*OBJECT_ID),
2090+
unresolved_superclass,
2091+
))
20952092
}
20962093

2097-
fn linearize_parent_class(
2094+
fn linearize_superclass(
20982095
&mut self,
2099-
definition_ids: &[DefinitionId],
2096+
declaration_id: DeclarationId,
21002097
context: &mut LinearizationContext,
2101-
) -> Ancestors {
2102-
let (picked_parent, unresolved_parent) = self.get_parent_class(definition_ids);
2103-
let mut result = self.linearize_ancestors(picked_parent, context);
2098+
) -> Option<Ancestors> {
2099+
let (superclass_id, unresolved_superclass) = self.get_superclass(declaration_id)?;
2100+
let mut result = self.linearize_ancestors(superclass_id, context);
21042101

2105-
if let Some(name_id) = unresolved_parent {
2102+
if let Some(name_id) = unresolved_superclass {
21062103
context.partial = true;
21072104

2108-
// Insert the unresolved parent as a Partial ancestor at the front of the chain, so it
2105+
// Insert the unresolved superclass as a Partial ancestor at the front of the chain, so it
21092106
// appears before the default Object ancestors
21102107
let ancestors = match &mut result {
21112108
Ancestors::Complete(ids) | Ancestors::Cyclic(ids) | Ancestors::Partial(ids) => ids,
21122109
};
21132110
ancestors.insert(0, Ancestor::Partial(name_id));
21142111

2115-
result.to_partial()
2116-
} else {
2117-
result
2112+
result = result.to_partial();
21182113
}
2114+
2115+
Some(result)
21192116
}
21202117

21212118
fn mixins_of(&self, definition_id: DefinitionId) -> Option<Vec<Mixin>> {

0 commit comments

Comments
 (0)