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
5 changes: 5 additions & 0 deletions baml_language/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 13 additions & 8 deletions baml_language/crates/baml_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub use baml_vm::{
BinOp, Bytecode, Class, CmpOp, Enum, Function, FunctionKind, Instruction, Object, Program,
UnaryOp, Value,
};
use baml_workspace::ProjectRoot;
use baml_workspace::Project;
pub use compiler::{ClassInfo, Compiler, compile_function};

/// Generate bytecode for all functions in a project.
Expand All @@ -39,7 +39,7 @@ pub use compiler::{ClassInfo, Compiler, compile_function};
/// It collects all functions from HIR, type-checks them via THIR,
/// and compiles them to bytecode.
#[salsa::tracked]
pub fn generate_project_bytecode(db: &dyn baml_thir::Db, root: ProjectRoot) -> Program {
pub fn generate_project_bytecode(db: &dyn baml_thir::Db, root: Project) -> Program {
let files = baml_workspace::project_files(db, root);
compile_files(db, &files)
}
Expand All @@ -61,7 +61,7 @@ pub fn compile_files(db: &dyn baml_thir::Db, files: &[SourceFile]) -> Program {
let items_struct = baml_hir::file_items(db, *file);
for item in items_struct.items(db) {
if let ItemId::Function(func_loc) = item {
let signature = function_signature(db, *file, *func_loc);
let signature = function_signature(db, *func_loc);
globals.insert(signature.name.to_string(), global_idx);
global_idx += 1;
}
Expand Down Expand Up @@ -111,12 +111,17 @@ pub fn compile_files(db: &dyn baml_thir::Db, files: &[SourceFile]) -> Program {
let items_struct = baml_hir::file_items(db, *file);
for item in items_struct.items(db) {
if let ItemId::Function(func_loc) = item {
let signature = function_signature(db, *file, *func_loc);
let body = function_body(db, *file, *func_loc);
let signature = function_signature(db, *func_loc);
let body = function_body(db, *func_loc);

// Run type inference
let inference =
baml_thir::infer_function(db, &signature, &body, Some(typing_context.clone()));
let inference = baml_thir::infer_function(
db,
&signature,
&body,
Some(typing_context.clone()),
None, // TODO: Pass class fields. Eventually remove this parameter.
);

// Get parameter names
let params: Vec<Name> = signature.params.iter().map(|p| p.name.clone()).collect();
Expand Down Expand Up @@ -177,7 +182,7 @@ fn build_typing_context<'db>(
let items_struct = baml_hir::file_items(db, *file);
for item in items_struct.items(db) {
if let ItemId::Function(func_loc) = item {
let signature = function_signature(db, *file, *func_loc);
let signature = function_signature(db, *func_loc);

// Build the arrow type: (param_types) -> return_type
let param_types: Vec<baml_thir::Ty<'db>> = signature
Expand Down
22 changes: 13 additions & 9 deletions baml_language/crates/baml_codegen/src/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@

use std::collections::HashMap;

use baml_db::{
RootDatabase, baml_hir, baml_thir, build_typing_context_from_files, function_body,
function_signature,
};
use baml_db::{RootDatabase, baml_hir, baml_thir};
use baml_hir::{function_body, function_signature};
use baml_thir::build_typing_context_from_files;
use baml_vm::test::{Instruction, Value};

use crate::ClassInfo;
Expand Down Expand Up @@ -193,7 +192,7 @@ fn compile_source(source: &str) -> CompileResult {
let mut global_idx = 0;
for item in items {
if let baml_hir::ItemId::Function(func_loc) = item {
let sig = function_signature(&db, file, *func_loc);
let sig = function_signature(&db, *func_loc);
globals.insert(sig.name.to_string(), global_idx);
global_idx += 1;
}
Expand Down Expand Up @@ -246,12 +245,17 @@ fn compile_source(source: &str) -> CompileResult {
let mut functions = Vec::new();
for item in items {
if let baml_hir::ItemId::Function(func_loc) = item {
let signature = function_signature(&db, file, *func_loc);
let body = function_body(&db, file, *func_loc);
let signature = function_signature(&db, *func_loc);
let body = function_body(&db, *func_loc);

// Run type inference
let inference =
baml_thir::infer_function(&db, &signature, &body, Some(typing_context.clone()));
let inference = baml_thir::infer_function(
&db,
&signature,
&body,
Some(typing_context.clone()),
None,
);

// Get parameter names
let params: Vec<baml_base::Name> =
Expand Down
67 changes: 6 additions & 61 deletions baml_language/crates/baml_db/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Root database that assembles all compiler phases.
//!
//! This crate purely combines all the compiler traits into a single database.
//! All testing happens in the separate `baml_tests` crate.

use std::{
path::PathBuf,
Expand Down Expand Up @@ -75,9 +74,12 @@ impl RootDatabase {
SourceFile::new(self, text.into(), path.into(), file_id)
}

/// Create a project root
pub fn set_project_root(&mut self, path: impl Into<PathBuf>) -> baml_workspace::ProjectRoot {
baml_workspace::ProjectRoot::new(self, path.into())
/// Create a project root with an empty file list.
///
/// After creating the project root, use `add_file()` to add source files,
/// then update the project root's file list with `root.set_files()`.
pub fn set_project_root(&mut self, path: impl Into<PathBuf>) -> baml_workspace::Project {
baml_workspace::Project::new(self, path.into(), vec![])
}
}

Expand All @@ -86,60 +88,3 @@ impl Default for RootDatabase {
Self::new()
}
}

//
// ────────────────────────────────────────────────── FUNCTION QUERIES ─────
//

// Re-export function queries from baml_hir
pub use baml_hir::{function_body, function_signature};

//
// ────────────────────────────────────────────────── TYPING CONTEXT ─────
//

/// Build typing context from a list of source files.
///
/// This maps function names to their arrow types, e.g.:
/// `Foo` -> `(int) -> int` for `function Foo(x: int) -> int`
///
/// This is used as the starting scope when type-checking function bodies,
/// allowing function calls to be properly typed.
///
/// Note: This is not a Salsa query because it returns `Ty<'db>` which contains
/// lifetime-parameterized data. Callers should cache the result if needed.
pub fn build_typing_context_from_files<'db>(
db: &'db dyn baml_thir::Db,
files: &[SourceFile],
) -> std::collections::HashMap<baml_base::Name, baml_thir::Ty<'db>> {
let mut context = std::collections::HashMap::new();

for file in files {
let items_struct = baml_hir::file_items(db, *file);
let items = items_struct.items(db);

for item in items {
if let baml_hir::ItemId::Function(func_loc) = item {
let signature = function_signature(db, *file, *func_loc);

// Build the arrow type: (param_types) -> return_type
let param_types: Vec<baml_thir::Ty<'db>> = signature
.params
.iter()
.map(|p| baml_thir::lower_type_ref(db, &p.type_ref))
.collect();

let return_type = baml_thir::lower_type_ref(db, &signature.return_type);

let func_type = baml_thir::Ty::Function {
params: param_types,
ret: Box::new(return_type),
};

context.insert(signature.name.clone(), func_type);
}
}
}

context
}
24 changes: 24 additions & 0 deletions baml_language/crates/baml_diagnostics/src/compiler_error.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
pub mod error_format;
pub mod name_error;
pub mod parse_error;
pub mod type_error;

use std::collections::HashMap;

use ariadne::{Report, ReportKind};
use baml_base::{FileId, Span};
pub use name_error::NameError;
pub use parse_error::ParseError;
pub use type_error::TypeError;

Expand All @@ -16,6 +18,7 @@ pub use type_error::TypeError;
pub enum CompilerError<Ty> {
ParseError(ParseError),
TypeError(TypeError<Ty>),
NameError(NameError),
}

pub struct ErrorCode(u32);
Expand Down Expand Up @@ -55,6 +58,7 @@ const NOT_INDEXABLE: ErrorCode = ErrorCode(8);

const UNEXPECTED_EOF: ErrorCode = ErrorCode(9);
const UNEXPECTED_TOKEN: ErrorCode = ErrorCode(10);
const DUPLICATE_NAME: ErrorCode = ErrorCode(11);

/// Render an ariadne Report to a String.
///
Expand Down Expand Up @@ -118,3 +122,23 @@ pub fn render_type_error<Ty: std::fmt::Display + Clone>(
let report = render_error(&color_mode, compiler_error);
render_report_to_string(&report, sources)
}

/// Convenience function to render a `NameError` directly to a string.
///
/// This combines `render_error` and `render_report_to_string` for the common case
/// of rendering name resolution errors.
pub fn render_name_error(
error: &NameError,
sources: &HashMap<FileId, String>,
color: bool,
) -> String {
let color_mode = if color {
ColorMode::Color
} else {
ColorMode::NoColor
};
// Use String as the type parameter since NameError doesn't use it
let compiler_error: CompilerError<String> = CompilerError::NameError(error.clone());
let report = render_error(&color_mode, compiler_error);
render_report_to_string(&report, sources)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use ariadne::{Label, ReportBuilder};
use baml_base::Span;

use super::{
ARGUMENT_COUNT_MISMATCH, CompilerError, ErrorCode, INVALID_OPERATOR, NO_SUCH_FIELD,
NOT_CALLABLE, NOT_INDEXABLE, ParseError, Report, ReportKind, TYPE_MISMATCH, TypeError,
UNEXPECTED_EOF, UNEXPECTED_TOKEN, UNKNOWN_TYPE, UNKNOWN_VARIABLE,
ARGUMENT_COUNT_MISMATCH, CompilerError, DUPLICATE_NAME, ErrorCode, INVALID_OPERATOR,
NO_SUCH_FIELD, NOT_CALLABLE, NOT_INDEXABLE, NameError, ParseError, Report, ReportKind,
TYPE_MISMATCH, TypeError, UNEXPECTED_EOF, UNEXPECTED_TOKEN, UNKNOWN_TYPE, UNKNOWN_VARIABLE,
};

/// The message format and id of each compiler error variant.
Expand Down Expand Up @@ -84,6 +84,28 @@ where
simple_error(format!("Type {ty} is not indexable"), span, NOT_INDEXABLE)
}
},
CompilerError::NameError(name_error) => match name_error {
NameError::DuplicateName {
name,
kind,
first,
first_path,
second,
second_path,
} => (
Report::build(ReportKind::Error, second)
.with_message(format!("Duplicate {kind} '{name}'"))
.with_label(
Label::new(second)
.with_message(format!("{kind} '{name}' defined in {second_path}")),
)
.with_label(
Label::new(first)
.with_message(format!("'{name}' previously defined in {first_path}")),
),
DUPLICATE_NAME,
),
},
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// ============================================================================
// Name Resolution Errors
// ============================================================================

use baml_base::Span;

/// Name resolution errors that can occur during compilation.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum NameError {
/// Duplicate definition of a name in the same namespace.
DuplicateName {
name: String,
kind: &'static str,
first: Span,
first_path: String,
second: Span,
second_path: String,
},
}
4 changes: 2 additions & 2 deletions baml_language/crates/baml_diagnostics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
pub mod compiler_error;

pub use compiler_error::{
ColorMode, CompilerError, ParseError, TypeError, render_error, render_parse_error,
render_report_to_string, render_type_error,
ColorMode, CompilerError, NameError, ParseError, TypeError, render_error, render_name_error,
render_parse_error, render_report_to_string, render_type_error,
};
1 change: 1 addition & 0 deletions baml_language/crates/baml_hir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ workspace = true

[dependencies]
baml_base = { workspace = true }
baml_diagnostics = { workspace = true }
baml_parser = { workspace = true }
baml_syntax = { workspace = true }
baml_workspace = { workspace = true }
Expand Down
Loading
Loading