Rename Top to Any and Bottom to Empty

Top/Bottom can be unintuitive or ambiguous.
This commit is contained in:
Max Bernstein 2025-03-06 15:43:52 -05:00 committed by Takashi Kokubun
parent bbbf40de3f
commit c5a93df555
Notes: git 2025-04-18 13:48:22 +00:00
4 changed files with 51 additions and 51 deletions

View File

@ -43,13 +43,13 @@ end
# ===== Start generating the type DAG =====
# Start at Top. All types are subtypes of Top.
top = Type.new "Top"
# Start at Any. All types are subtypes of Any.
any = Type.new "Any"
# Build the Ruby object universe.
object = top.subtype "Object"
object = any.subtype "Object"
object.subtype "ObjectExact"
$object_user = object.subtype "ObjectUser"
$user = top.subtype "User"
$user = any.subtype "User"
$builtin_exact = object.subtype "BuiltinExact"
# Define a new type that can be subclassed (most of them).
@ -86,7 +86,7 @@ base_type "TrueClass"
base_type "FalseClass"
# Build the primitive object universe.
primitive = top.subtype "Primitive"
primitive = any.subtype "Primitive"
primitive.subtype "CBool"
primitive.subtype "CPtr"
primitive.subtype "CDouble"
@ -101,9 +101,9 @@ unsigned = primitive_int.subtype "CUnsigned"
# Assign individual bits to type leaves and union bit patterns to nodes with subtypes
num_bits = 0
bits = {"Bottom" => ["0u64"]}
numeric_bits = {"Bottom" => 0}
Set[top, *top.all_subtypes].sort_by(&:name).each {|type|
bits = {"Empty" => ["0u64"]}
numeric_bits = {"Empty" => 0}
Set[any, *any.all_subtypes].sort_by(&:name).each {|type|
subtypes = type.subtypes
if subtypes.empty?
# Assign bits for leaves
@ -115,7 +115,7 @@ Set[top, *top.all_subtypes].sort_by(&:name).each {|type|
bits[type.name] = subtypes.map(&:name).sort
end
}
[*top.all_subtypes, top].each {|type|
[*any.all_subtypes, any].each {|type|
subtypes = type.subtypes
unless subtypes.empty?
numeric_bits[type.name] = subtypes.map {|ty| numeric_bits[ty.name]}.reduce(&:|)

View File

@ -1,10 +1,10 @@
// This file is @generated by src/gen_hir_type.rb.
mod bits {
pub const Any: u64 = Object | Primitive | User;
pub const Array: u64 = ArrayExact | ArrayUser;
pub const ArrayExact: u64 = 1u64 << 0;
pub const ArrayUser: u64 = 1u64 << 1;
pub const Bignum: u64 = 1u64 << 2;
pub const Bottom: u64 = 0u64;
pub const BuiltinExact: u64 = ArrayExact | FalseClassExact | FloatExact | HashExact | IntegerExact | NilClassExact | StringExact | SymbolExact | TrueClassExact;
pub const CBool: u64 = 1u64 << 3;
pub const CDouble: u64 = 1u64 << 4;
@ -22,6 +22,7 @@ mod bits {
pub const CUInt8: u64 = 1u64 << 14;
pub const CUnsigned: u64 = CUInt16 | CUInt32 | CUInt64 | CUInt8;
pub const DynamicSymbol: u64 = 1u64 << 15;
pub const Empty: u64 = 0u64;
pub const FalseClass: u64 = FalseClassExact | FalseClassUser;
pub const FalseClassExact: u64 = 1u64 << 16;
pub const FalseClassUser: u64 = 1u64 << 17;
@ -51,13 +52,12 @@ mod bits {
pub const Symbol: u64 = SymbolExact | SymbolUser;
pub const SymbolExact: u64 = DynamicSymbol | StaticSymbol;
pub const SymbolUser: u64 = 1u64 << 31;
pub const Top: u64 = Object | Primitive | User;
pub const TrueClass: u64 = TrueClassExact | TrueClassUser;
pub const TrueClassExact: u64 = 1u64 << 32;
pub const TrueClassUser: u64 = 1u64 << 33;
pub const User: u64 = ArrayUser | FalseClassUser | FloatUser | HashUser | IntegerUser | NilClassUser | StringUser | SymbolUser | TrueClassUser;
pub const AllBitPatterns: [(&'static str, u64); 56] = [
("Top", Top),
("Any", Any),
("Object", Object),
("ObjectUser", ObjectUser),
("TrueClass", TrueClass),
@ -112,17 +112,17 @@ mod bits {
("Array", Array),
("ArrayUser", ArrayUser),
("ArrayExact", ArrayExact),
("Bottom", Bottom),
("Empty", Empty),
];
pub const NumTypeBits: u64 = 34;
}
pub mod types {
use super::*;
pub const Any: Type = Type::from_bits(bits::Any);
pub const Array: Type = Type::from_bits(bits::Array);
pub const ArrayExact: Type = Type::from_bits(bits::ArrayExact);
pub const ArrayUser: Type = Type::from_bits(bits::ArrayUser);
pub const Bignum: Type = Type::from_bits(bits::Bignum);
pub const Bottom: Type = Type::from_bits(bits::Bottom);
pub const BuiltinExact: Type = Type::from_bits(bits::BuiltinExact);
pub const CBool: Type = Type::from_bits(bits::CBool);
pub const CDouble: Type = Type::from_bits(bits::CDouble);
@ -140,6 +140,7 @@ pub mod types {
pub const CUInt8: Type = Type::from_bits(bits::CUInt8);
pub const CUnsigned: Type = Type::from_bits(bits::CUnsigned);
pub const DynamicSymbol: Type = Type::from_bits(bits::DynamicSymbol);
pub const Empty: Type = Type::from_bits(bits::Empty);
pub const FalseClass: Type = Type::from_bits(bits::FalseClass);
pub const FalseClassExact: Type = Type::from_bits(bits::FalseClassExact);
pub const FalseClassUser: Type = Type::from_bits(bits::FalseClassUser);
@ -169,7 +170,6 @@ pub mod types {
pub const Symbol: Type = Type::from_bits(bits::Symbol);
pub const SymbolExact: Type = Type::from_bits(bits::SymbolExact);
pub const SymbolUser: Type = Type::from_bits(bits::SymbolUser);
pub const Top: Type = Type::from_bits(bits::Top);
pub const TrueClass: Type = Type::from_bits(bits::TrueClass);
pub const TrueClassExact: Type = Type::from_bits(bits::TrueClassExact);
pub const TrueClassUser: Type = Type::from_bits(bits::TrueClassUser);

View File

@ -17,7 +17,7 @@ use crate::cruby::ClassRelationship;
/// Type internals.
pub enum Specialization {
/// We know nothing about the specialization of this Type.
Top,
Any,
/// We know that this Type is an instance of the given Ruby class in the VALUE or any of its subclasses.
Type(VALUE),
/// We know that this Type is an instance of exactly the Ruby class in the VALUE.
@ -29,9 +29,9 @@ pub enum Specialization {
Int(u64),
/// We know that this Type is exactly the given primitive/C double.
Double(f64),
/// We know that the Type is [`types::Bottom`] and therefore the instruction that produces this
/// We know that the Type is [`types::Empty`] and therefore the instruction that produces this
/// value never returns.
Bottom,
Empty,
}
// NOTE: Type very intentionally does not support Eq or PartialEq; we almost never want to check
@ -47,8 +47,8 @@ pub enum Specialization {
pub struct Type {
/// A bitset representing type information about the object. Specific bits are assigned for
/// leaf types (for example, static symbols) and union-ing bitsets together represents
/// union-ing sets of types. These sets form a lattice (with Top as "could be anything" and
/// Bottom as "can be nothing").
/// union-ing sets of types. These sets form a lattice (with Any as "could be anything" and
/// Empty as "can be nothing").
///
/// Capable of also representing primitive types (bool, i32, etc).
///
@ -76,7 +76,7 @@ fn get_class_name(class: Option<VALUE>) -> String {
fn write_spec(f: &mut std::fmt::Formatter, ty: Type) -> std::fmt::Result {
match ty.spec {
Specialization::Top | Specialization::Bottom => { Ok(()) },
Specialization::Any | Specialization::Empty => { Ok(()) },
Specialization::Object(val) => write!(f, "[{val}]"),
Specialization::Type(val) => write!(f, "[class:{}]", get_class_name(Some(val))),
Specialization::TypeExact(val) => write!(f, "[class_exact:{}]", get_class_name(Some(val))),
@ -175,10 +175,10 @@ impl Type {
const fn from_bits(bits: u64) -> Type {
Type {
bits,
spec: if bits == bits::Bottom {
Specialization::Bottom
spec: if bits == bits::Empty {
Specialization::Empty
} else {
Specialization::Top
Specialization::Any
},
}
}
@ -187,7 +187,7 @@ impl Type {
/// `specialization` represents. For example, `Type::from_cint(types::CBool, 1)` or
/// `Type::from_cint(types::CUInt16, 12)`.
pub fn from_cint(ty: Type, val: i64) -> Type {
assert_eq!(ty.spec, Specialization::Top);
assert_eq!(ty.spec, Specialization::Any);
assert!((ty.is_subtype(types::CUnsigned) || ty.is_subtype(types::CSigned)) &&
ty.bits != types::CUnsigned.bits && ty.bits != types::CSigned.bits,
"ty must be a specific int size");
@ -302,10 +302,10 @@ impl Type {
/// You probably want [`Type::is_subtype`] instead.
fn spec_is_subtype_of(&self, other: Type) -> bool {
match (self.spec, other.spec) {
// Bottom is a subtype of everything; Top is a supertype of everything
(Specialization::Bottom, _) | (_, Specialization::Top) => true,
// Other is not Top from the previous case, so Top is definitely not a subtype
(Specialization::Top, _) | (_, Specialization::Bottom) => false,
// Empty is a subtype of everything; Any is a supertype of everything
(Specialization::Empty, _) | (_, Specialization::Any) => true,
// Other is not Any from the previous case, so Any is definitely not a subtype
(Specialization::Any, _) | (_, Specialization::Empty) => false,
// Int and double specialization requires exact equality
(Specialization::Int(_), _) | (_, Specialization::Int(_)) |
(Specialization::Double(_), _) | (_, Specialization::Double(_)) =>
@ -348,27 +348,27 @@ mod tests {
}
#[test]
fn bottom_is_subtype_of_everything() {
fn empty_is_subtype_of_everything() {
// Spot check a few cases
assert_subtype(types::Bottom, types::NilClassExact);
assert_subtype(types::Bottom, types::Array);
assert_subtype(types::Bottom, types::Object);
assert_subtype(types::Bottom, types::CUInt16);
assert_subtype(types::Bottom, Type::from_cint(types::CInt32, 10));
assert_subtype(types::Bottom, types::Top);
assert_subtype(types::Bottom, types::Bottom);
assert_subtype(types::Empty, types::NilClassExact);
assert_subtype(types::Empty, types::Array);
assert_subtype(types::Empty, types::Object);
assert_subtype(types::Empty, types::CUInt16);
assert_subtype(types::Empty, Type::from_cint(types::CInt32, 10));
assert_subtype(types::Empty, types::Any);
assert_subtype(types::Empty, types::Empty);
}
#[test]
fn everything_is_a_subtype_of_top() {
fn everything_is_a_subtype_of_any() {
// Spot check a few cases
assert_subtype(types::NilClassExact, types::Top);
assert_subtype(types::Array, types::Top);
assert_subtype(types::Object, types::Top);
assert_subtype(types::CUInt16, types::Top);
assert_subtype(Type::from_cint(types::CInt32, 10), types::Top);
assert_subtype(types::Bottom, types::Top);
assert_subtype(types::Top, types::Top);
assert_subtype(types::NilClassExact, types::Any);
assert_subtype(types::Array, types::Any);
assert_subtype(types::Object, types::Any);
assert_subtype(types::CUInt16, types::Any);
assert_subtype(Type::from_cint(types::CInt32, 10), types::Any);
assert_subtype(types::Empty, types::Any);
assert_subtype(types::Any, types::Any);
}
#[test]
@ -499,7 +499,7 @@ mod tests {
#[test]
fn union_bits_unions_bits() {
assert_bit_equal(types::Fixnum.union(types::StaticSymbol), Type { bits: bits::Fixnum | bits::StaticSymbol, spec: Specialization::Top });
assert_bit_equal(types::Fixnum.union(types::StaticSymbol), Type { bits: bits::Fixnum | bits::StaticSymbol, spec: Specialization::Any });
}
#[test]
@ -517,8 +517,8 @@ mod tests {
crate::cruby::with_rubyvm(|| {
let specialized = Type::from_value(unsafe { rb_ary_new_capa(0) });
let unspecialized = types::StringExact;
assert_bit_equal(specialized.union(unspecialized), Type { bits: bits::ArrayExact | bits::StringExact, spec: Specialization::Top });
assert_bit_equal(unspecialized.union(specialized), Type { bits: bits::ArrayExact | bits::StringExact, spec: Specialization::Top });
assert_bit_equal(specialized.union(unspecialized), Type { bits: bits::ArrayExact | bits::StringExact, spec: Specialization::Any });
assert_bit_equal(unspecialized.union(specialized), Type { bits: bits::ArrayExact | bits::StringExact, spec: Specialization::Any });
});
}
@ -571,7 +571,7 @@ mod tests {
crate::cruby::with_rubyvm(|| {
let string = Type::from_value(rust_str_to_ruby("hello"));
let array = Type::from_value(unsafe { rb_ary_new_capa(0) });
assert_bit_equal(string.union(array), Type { bits: bits::ArrayExact | bits::StringExact, spec: Specialization::Top });
assert_bit_equal(string.union(array), Type { bits: bits::ArrayExact | bits::StringExact, spec: Specialization::Any });
});
}

View File

@ -4,7 +4,7 @@
use core::ffi::c_void;
use std::collections::HashMap;
use crate::{cruby::*, hir_type::{types::{Bottom, Fixnum}, Type}};
use crate::{cruby::*, hir_type::{types::{Empty, Fixnum}, Type}};
/// Ephemeral state for profiling runtime information
struct Profiler {
@ -67,7 +67,7 @@ fn profile_operands(profiler: &mut Profiler, n: usize) {
let mut types = if let Some(types) = payload.opnd_types.get(&profiler.insn_idx) {
types.clone()
} else {
vec![Bottom; n]
vec![Empty; n]
};
for i in 0..n {