Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions crates/plotnik-lib/src/query/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::type_system::TypeKind;

use super::query::LinkedQuery;
use super::type_check::{
FieldInfo, TYPE_NODE, TYPE_STRING, TYPE_VOID, TypeContext, TypeId, TypeKind as InferredTypeKind,
FieldInfo, TYPE_NODE, TYPE_STRING, TYPE_VOID, TypeContext, TypeId, TypeShape,
};

/// Error during bytecode emission.
Expand Down Expand Up @@ -236,18 +236,18 @@ impl TypeTableBuilder {
&mut self,
slot_index: usize,
_type_id: TypeId,
type_kind: &InferredTypeKind,
type_kind: &TypeShape,
type_ctx: &TypeContext,
interner: &Interner,
strings: &mut StringTableBuilder,
) -> Result<(), EmitError> {
match type_kind {
InferredTypeKind::Void | InferredTypeKind::Node | InferredTypeKind::String => {
TypeShape::Void | TypeShape::Node | TypeShape::String => {
// Builtins - should not reach here
unreachable!("builtins should be handled separately")
}

InferredTypeKind::Custom(sym) => {
TypeShape::Custom(sym) => {
// Custom type annotation: @x :: Identifier → type Identifier = Node
let bc_type_id = QTypeId::from_custom_index(slot_index);

Expand All @@ -266,7 +266,7 @@ impl TypeTableBuilder {
Ok(())
}

InferredTypeKind::Optional(inner) => {
TypeShape::Optional(inner) => {
let inner_bc = self.resolve_type(*inner, type_ctx)?;

self.type_defs[slot_index] = TypeDef {
Expand All @@ -277,7 +277,7 @@ impl TypeTableBuilder {
Ok(())
}

InferredTypeKind::Array { element, non_empty } => {
TypeShape::Array { element, non_empty } => {
let element_bc = self.resolve_type(*element, type_ctx)?;

let kind = if *non_empty {
Expand All @@ -293,7 +293,7 @@ impl TypeTableBuilder {
Ok(())
}

InferredTypeKind::Struct(fields) => {
TypeShape::Struct(fields) => {
// Resolve field types (this may create Optional wrappers at later indices)
let mut resolved_fields = Vec::with_capacity(fields.len());
for (field_sym, field_info) in fields {
Expand All @@ -320,7 +320,7 @@ impl TypeTableBuilder {
Ok(())
}

InferredTypeKind::Enum(variants) => {
TypeShape::Enum(variants) => {
// Resolve variant types (this may create types at later indices)
let mut resolved_variants = Vec::with_capacity(variants.len());
for (variant_sym, variant_type_id) in variants {
Expand All @@ -347,7 +347,7 @@ impl TypeTableBuilder {
Ok(())
}

InferredTypeKind::Ref(_def_id) => {
TypeShape::Ref(_def_id) => {
// Ref types are not emitted - they resolve to their target
unreachable!("Ref types should not be collected for emission")
}
Expand All @@ -363,7 +363,7 @@ impl TypeTableBuilder {

// Handle Ref types by following the reference
if let Some(type_kind) = type_ctx.get_type(type_id)
&& let InferredTypeKind::Ref(def_id) = type_kind
&& let TypeShape::Ref(def_id) = type_kind
&& let Some(def_type_id) = type_ctx.get_def_type(*def_id)
{
return self.resolve_type(def_type_id, type_ctx);
Expand Down Expand Up @@ -491,7 +491,7 @@ fn collect_types_dfs(
};

// Resolve Ref types to their target
if let InferredTypeKind::Ref(def_id) = type_kind {
if let TypeShape::Ref(def_id) = type_kind {
if let Some(target_id) = type_ctx.get_def_type(*def_id) {
collect_types_dfs(target_id, type_ctx, out, seen);
}
Expand All @@ -502,29 +502,29 @@ fn collect_types_dfs(

// Collect children first (depth-first), then add self
match type_kind {
InferredTypeKind::Struct(fields) => {
TypeShape::Struct(fields) => {
for field_info in fields.values() {
collect_types_dfs(field_info.type_id, type_ctx, out, seen);
}
out.push(type_id);
}
InferredTypeKind::Enum(variants) => {
TypeShape::Enum(variants) => {
for &variant_type_id in variants.values() {
collect_types_dfs(variant_type_id, type_ctx, out, seen);
}
out.push(type_id);
}
InferredTypeKind::Array { element, .. } => {
TypeShape::Array { element, .. } => {
// Collect element type first, then add the Array itself
collect_types_dfs(*element, type_ctx, out, seen);
out.push(type_id);
}
InferredTypeKind::Optional(inner) => {
TypeShape::Optional(inner) => {
// Collect inner type first, then add the Optional itself
collect_types_dfs(*inner, type_ctx, out, seen);
out.push(type_id);
}
InferredTypeKind::Custom(_) => {
TypeShape::Custom(_) => {
// Custom types alias Node, no children to collect
out.push(type_id);
}
Expand Down
50 changes: 25 additions & 25 deletions crates/plotnik-lib/src/query/type_check/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ use crate::parser::ast::Expr;

use super::symbol::{DefId, Interner, Symbol};
use super::types::{
Arity, FieldInfo, TYPE_NODE, TYPE_STRING, TYPE_VOID, TermInfo, TypeId, TypeKind,
Arity, FieldInfo, TYPE_NODE, TYPE_STRING, TYPE_VOID, TermInfo, TypeId, TypeShape,
};

/// Central registry for types, symbols, and expression metadata.
#[derive(Clone, Debug)]
pub struct TypeContext {
types: Vec<TypeKind>,
type_map: HashMap<TypeKind, TypeId>,
types: Vec<TypeShape>,
type_map: HashMap<TypeShape, TypeId>,

def_names: Vec<Symbol>,
def_ids: HashMap<Symbol, DefId>,
Expand Down Expand Up @@ -48,13 +48,13 @@ impl TypeContext {
};

// Pre-register builtin types at their expected IDs
let void_id = ctx.intern_type(TypeKind::Void);
let void_id = ctx.intern_type(TypeShape::Void);
debug_assert_eq!(void_id, TYPE_VOID);

let node_id = ctx.intern_type(TypeKind::Node);
let node_id = ctx.intern_type(TypeShape::Node);
debug_assert_eq!(node_id, TYPE_NODE);

let string_id = ctx.intern_type(TypeKind::String);
let string_id = ctx.intern_type(TypeShape::String);
debug_assert_eq!(string_id, TYPE_STRING);

ctx
Expand All @@ -68,42 +68,42 @@ impl TypeContext {
}

/// Intern a type, returning its ID. Deduplicates identical types.
pub fn intern_type(&mut self, kind: TypeKind) -> TypeId {
if let Some(&id) = self.type_map.get(&kind) {
pub fn intern_type(&mut self, shape: TypeShape) -> TypeId {
if let Some(&id) = self.type_map.get(&shape) {
return id;
}

let id = TypeId(self.types.len() as u32);
self.types.push(kind.clone());
self.type_map.insert(kind, id);
self.types.push(shape.clone());
self.type_map.insert(shape, id);
id
}

/// Get the TypeKind for a TypeId.
pub fn get_type(&self, id: TypeId) -> Option<&TypeKind> {
/// Get the TypeShape for a TypeId.
pub fn get_type(&self, id: TypeId) -> Option<&TypeShape> {
self.types.get(id.0 as usize)
}

/// Get or create a type, returning both the ID and a reference.
pub fn get_or_intern(&mut self, kind: TypeKind) -> (TypeId, &TypeKind) {
let id = self.intern_type(kind);
pub fn get_or_intern(&mut self, shape: TypeShape) -> (TypeId, &TypeShape) {
let id = self.intern_type(shape);
(id, &self.types[id.0 as usize])
}

/// Intern a struct type from fields.
pub fn intern_struct(&mut self, fields: BTreeMap<Symbol, FieldInfo>) -> TypeId {
self.intern_type(TypeKind::Struct(fields))
self.intern_type(TypeShape::Struct(fields))
}

/// Intern a struct type with a single field.
pub fn intern_single_field(&mut self, name: Symbol, info: FieldInfo) -> TypeId {
self.intern_type(TypeKind::Struct(BTreeMap::from([(name, info)])))
self.intern_type(TypeShape::Struct(BTreeMap::from([(name, info)])))
}

/// Get struct fields from a TypeId, if it points to a Struct.
pub fn get_struct_fields(&self, id: TypeId) -> Option<&BTreeMap<Symbol, FieldInfo>> {
match self.get_type(id)? {
TypeKind::Struct(fields) => Some(fields),
TypeShape::Struct(fields) => Some(fields),
_ => None,
}
}
Expand Down Expand Up @@ -199,7 +199,7 @@ impl TypeContext {
}

/// Iterate over all interned types.
pub fn iter_types(&self) -> impl Iterator<Item = (TypeId, &TypeKind)> {
pub fn iter_types(&self) -> impl Iterator<Item = (TypeId, &TypeShape)> {
self.types
.iter()
.enumerate()
Expand Down Expand Up @@ -239,17 +239,17 @@ mod tests {
fn builtin_types_have_correct_ids() {
let ctx = TypeContext::new();

assert_eq!(ctx.get_type(TYPE_VOID), Some(&TypeKind::Void));
assert_eq!(ctx.get_type(TYPE_NODE), Some(&TypeKind::Node));
assert_eq!(ctx.get_type(TYPE_STRING), Some(&TypeKind::String));
assert_eq!(ctx.get_type(TYPE_VOID), Some(&TypeShape::Void));
assert_eq!(ctx.get_type(TYPE_NODE), Some(&TypeShape::Node));
assert_eq!(ctx.get_type(TYPE_STRING), Some(&TypeShape::String));
}

#[test]
fn type_interning_deduplicates() {
let mut ctx = TypeContext::new();

let id1 = ctx.intern_type(TypeKind::Node);
let id2 = ctx.intern_type(TypeKind::Node);
let id1 = ctx.intern_type(TypeShape::Node);
let id2 = ctx.intern_type(TypeShape::Node);

assert_eq!(id1, id2);
assert_eq!(id1, TYPE_NODE);
Expand All @@ -264,8 +264,8 @@ mod tests {
let mut fields = BTreeMap::new();
fields.insert(x_sym, FieldInfo::required(TYPE_NODE));

let id1 = ctx.intern_type(TypeKind::Struct(fields.clone()));
let id2 = ctx.intern_type(TypeKind::Struct(fields));
let id1 = ctx.intern_type(TypeShape::Struct(fields.clone()));
let id2 = ctx.intern_type(TypeShape::Struct(fields));

assert_eq!(id1, id2);
}
Expand Down
16 changes: 8 additions & 8 deletions crates/plotnik-lib/src/query/type_check/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rowan::TextRange;
use super::context::TypeContext;
use super::symbol::Symbol;
use super::types::{
Arity, FieldInfo, QuantifierKind, TYPE_NODE, TYPE_STRING, TermInfo, TypeFlow, TypeId, TypeKind,
Arity, FieldInfo, QuantifierKind, TYPE_NODE, TYPE_STRING, TermInfo, TypeFlow, TypeId, TypeShape,
};
use super::unify::{UnifyError, unify_flows};

Expand Down Expand Up @@ -224,7 +224,7 @@ impl<'a, 'd> InferenceVisitor<'a, 'd> {
variants.insert(label_sym, self.flow_to_type(&body_info.flow));
}

let enum_type = self.ctx.intern_type(TypeKind::Enum(variants));
let enum_type = self.ctx.intern_type(TypeShape::Enum(variants));
TermInfo::new(combined_arity, TypeFlow::Scalar(enum_type))
}

Expand Down Expand Up @@ -373,7 +373,7 @@ impl<'a, 'd> InferenceVisitor<'a, 'd> {
TYPE_STRING
} else {
let sym = self.interner.intern(text);
self.ctx.intern_type(TypeKind::Custom(sym))
self.ctx.intern_type(TypeShape::Custom(sym))
}
})
})
Expand Down Expand Up @@ -426,7 +426,7 @@ impl<'a, 'd> InferenceVisitor<'a, 'd> {
let sym = self.interner.intern(name);
let def_id = self.ctx.get_def_id_sym(sym)?;
if self.ctx.is_recursive(def_id) {
Some(self.ctx.intern_type(TypeKind::Ref(def_id)))
Some(self.ctx.intern_type(TypeShape::Ref(def_id)))
} else {
None
}
Expand Down Expand Up @@ -474,7 +474,7 @@ impl<'a, 'd> InferenceVisitor<'a, 'd> {
fn make_flow_optional(&mut self, flow: TypeFlow) -> TypeFlow {
match flow {
TypeFlow::Void => TypeFlow::Void,
TypeFlow::Scalar(t) => TypeFlow::Scalar(self.ctx.intern_type(TypeKind::Optional(t))),
TypeFlow::Scalar(t) => TypeFlow::Scalar(self.ctx.intern_type(TypeShape::Optional(t))),
TypeFlow::Bubble(type_id) => {
let fields = self
.ctx
Expand All @@ -495,11 +495,11 @@ impl<'a, 'd> InferenceVisitor<'a, 'd> {
TypeFlow::Void => {
// Scalar list: void inner -> array of Node (or Ref)
let element = self.get_recursive_ref_type(inner).unwrap_or(TYPE_NODE);
let array_type = self.ctx.intern_type(TypeKind::Array { element, non_empty });
let array_type = self.ctx.intern_type(TypeShape::Array { element, non_empty });
TypeFlow::Scalar(array_type)
}
TypeFlow::Scalar(t) => {
let array_type = self.ctx.intern_type(TypeKind::Array {
let array_type = self.ctx.intern_type(TypeShape::Array {
element: t,
non_empty,
});
Expand All @@ -508,7 +508,7 @@ impl<'a, 'd> InferenceVisitor<'a, 'd> {
TypeFlow::Bubble(struct_type) => {
// Note: Bubble with * or + is strictly invalid unless it's a row capture,
// but we construct a valid type as fallback.
let array_type = self.ctx.intern_type(TypeKind::Array {
let array_type = self.ctx.intern_type(TypeShape::Array {
element: struct_type,
non_empty,
});
Expand Down
2 changes: 1 addition & 1 deletion crates/plotnik-lib/src/query/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub use context::TypeContext;
pub use symbol::{DefId, Interner, Symbol};
pub use types::{
Arity, FieldInfo, QuantifierKind, TYPE_NODE, TYPE_STRING, TYPE_VOID, TermInfo, TypeFlow,
TypeId, TypeKind,
TypeId, TypeShape,
};
pub use unify::{UnifyError, unify_flow, unify_flows};

Expand Down
10 changes: 7 additions & 3 deletions crates/plotnik-lib/src/query/type_check/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ impl TypeId {
}
}

/// The kind of a type, determining its structure.
/// The shape of an inferred type, determining its structure.
///
/// This represents the inference-time type representation which carries
/// actual data (fields, variants, inner types). Distinct from
/// `type_system::TypeKind` which is the bytecode format discriminant.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeKind {
pub enum TypeShape {
/// Produces nothing, transparent to parent scope.
Void,
/// A tree-sitter node.
Expand All @@ -52,7 +56,7 @@ pub enum TypeKind {
Ref(DefId),
}

impl TypeKind {
impl TypeShape {
pub fn is_void(&self) -> bool {
matches!(self, Self::Void)
}
Expand Down