diff --git a/.cargo/config.toml b/.cargo/config.toml index 9583e86..42ffed4 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -9,6 +9,7 @@ rustflags = ["-L", "native=external/lib/linux"] [target.'cfg(target_os = "linux")'.env] LD_LIBRARY_PATH = { value = "external/lib/linux", relative = true } +LLVM_SYS_211_PREFIX = "/usr/lib/llvm-21" [target.x86_64-pc-windows-gnu] linker = "x86_64-w64-mingw32-gcc" diff --git a/core/ast/src/builder.rs b/core/ast/src/builder.rs index 426b005..79fbc56 100644 --- a/core/ast/src/builder.rs +++ b/core/ast/src/builder.rs @@ -241,7 +241,7 @@ impl<'a> Builder<'a> { let node = Rc::new(SpecDefinition::new( id, - Visibility::default(), + Self::get_visibility(node), name, definitions, location, diff --git a/core/type-checker/src/symbol_table.rs b/core/type-checker/src/symbol_table.rs index 132508f..50d7114 100644 --- a/core/type-checker/src/symbol_table.rs +++ b/core/type-checker/src/symbol_table.rs @@ -165,10 +165,18 @@ pub(crate) enum Symbol { TypeAlias(TypeInfo), Struct(StructInfo), Enum(EnumInfo), - Spec(String), + Spec(SpecInfo), Function(FuncInfo), } +/// Information about a spec definition. +#[derive(Debug, Clone)] +pub(crate) struct SpecInfo { + pub(crate) name: String, + pub(crate) visibility: Visibility, + pub(crate) definition_scope_id: u32, +} + impl Symbol { #[allow(dead_code)] #[must_use = "discarding the name has no effect"] @@ -177,7 +185,7 @@ impl Symbol { Symbol::TypeAlias(ti) => ti.to_string(), Symbol::Struct(info) => info.name.clone(), Symbol::Enum(info) => info.name.clone(), - Symbol::Spec(name) => name.clone(), + Symbol::Spec(info) => info.name.clone(), Symbol::Function(sig) => sig.name.clone(), } } @@ -221,8 +229,8 @@ impl Symbol { kind: crate::type_info::TypeInfoKind::Enum(info.name.clone()), type_params: vec![], }), - Symbol::Spec(name) => Some(TypeInfo { - kind: crate::type_info::TypeInfoKind::Spec(name.clone()), + Symbol::Spec(info) => Some(TypeInfo { + kind: crate::type_info::TypeInfoKind::Spec(info.name.clone()), type_params: vec![], }), Symbol::Function(_) => None, @@ -232,14 +240,14 @@ impl Symbol { /// Check if this symbol has public visibility. /// /// Structs, Enums, and Functions respect their visibility field. - /// Type aliases and Specs are currently treated as public. + /// Type aliases and Specs respect their visibility field. #[must_use = "this is a pure check with no side effects"] pub(crate) fn is_public(&self) -> bool { match self { Symbol::TypeAlias(_) => true, Symbol::Struct(info) => matches!(info.visibility, Visibility::Public), Symbol::Enum(info) => matches!(info.visibility, Visibility::Public), - Symbol::Spec(_) => true, + Symbol::Spec(info) => matches!(info.visibility, Visibility::Public), Symbol::Function(sig) => matches!(sig.visibility, Visibility::Public), } } @@ -565,11 +573,17 @@ impl SymbolTable { } } - pub(crate) fn register_spec(&mut self, name: &str) -> anyhow::Result<()> { + pub(crate) fn register_spec(&mut self, name: &str, visibility: Visibility) -> anyhow::Result<()> { if let Some(scope) = &self.current_scope { + let scope_id = scope.borrow().id; + let spec_info = SpecInfo { + name: name.to_string(), + visibility, + definition_scope_id: scope_id, + }; scope .borrow_mut() - .insert_symbol(name, Symbol::Spec(name.to_string())) + .insert_symbol(name, Symbol::Spec(spec_info)) } else { bail!("No active scope to register spec") } @@ -883,7 +897,7 @@ impl SymbolTable { self.register_enum(&e.name(), &variants, e.visibility.clone())?; } Definition::Spec(sp) => { - self.register_spec(&sp.name())?; + self.register_spec(&sp.name(), sp.visibility.clone())?; } Definition::Function(f) => { let type_params = f diff --git a/core/type-checker/src/type_checker.rs b/core/type-checker/src/type_checker.rs index 82de1b2..64953ee 100644 --- a/core/type-checker/src/type_checker.rs +++ b/core/type-checker/src/type_checker.rs @@ -236,7 +236,7 @@ impl TypeChecker { } Definition::Spec(spec_definition) => { self.symbol_table - .register_spec(&spec_definition.name()) + .register_spec(&spec_definition.name(), spec_definition.visibility.clone()) .unwrap_or_else(|_| { self.errors.push(TypeCheckError::RegistrationFailed { kind: RegistrationKind::Spec, @@ -1673,7 +1673,7 @@ impl TypeChecker { } Definition::Spec(spec_definition) => { self.symbol_table - .register_spec(&spec_definition.name()) + .register_spec(&spec_definition.name(), spec_definition.visibility.clone()) .unwrap_or_else(|_| { self.errors.push(TypeCheckError::RegistrationFailed { kind: RegistrationKind::Spec, diff --git a/tests/src/ast/builder_features.rs b/tests/src/ast/builder_features.rs index 552b95a..150b81b 100644 --- a/tests/src/ast/builder_features.rs +++ b/tests/src/ast/builder_features.rs @@ -993,7 +993,24 @@ fn test_parse_spec_definition_visibility_private() { assert_eq!( spec.visibility, Visibility::Private, - "Spec definitions should always be private (no grammar support for pub)" + "Spec without pub should have Private visibility" + ); + } else { + panic!("Expected spec definition"); + } +} + +#[test] +fn test_parse_spec_definition_visibility_public() { + let source = r#"pub spec MySpec { fn verify() -> bool { return true; } }"#; + let arena = build_ast(source.to_string()); + let specs = arena.filter_nodes(|node| matches!(node, AstNode::Definition(Definition::Spec(_)))); + assert_eq!(specs.len(), 1, "Should find 1 spec definition"); + if let AstNode::Definition(Definition::Spec(spec)) = &specs[0] { + assert_eq!( + spec.visibility, + Visibility::Public, + "Spec with pub should have Public visibility" ); } else { panic!("Expected spec definition");