Skip to content
Open
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
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion core/ast/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ impl<'a> Builder<'a> {

let node = Rc::new(SpecDefinition::new(
id,
Visibility::default(),
Self::get_visibility(node),
name,
definitions,
location,
Expand Down
32 changes: 23 additions & 9 deletions core/type-checker/src/symbol_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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(),
}
}
Expand Down Expand Up @@ -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,
Expand All @@ -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),
}
}
Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions core/type-checker/src/type_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 18 additions & 1 deletion tests/src/ast/builder_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down