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
18 changes: 9 additions & 9 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ resolver = "2"
ra_ap_proc_macro_api = { path = "crates/third_party/ra_ap_proc_macro_api" }

[workspace.package]
version = "0.3.0-rc19"
version = "0.3.0-rc20"
description = "The Incan programming language compiler"
edition = "2024"
rust-version = "1.92"
Expand Down
86 changes: 64 additions & 22 deletions src/backend/ir/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,11 @@ impl<'a> IrCodegen<'a> {
fn canonical_registry_for_programs<'program>(
programs: impl IntoIterator<Item = (&'program [String], &'program IrProgram)>,
) -> FunctionRegistry {
let programs: Vec<_> = programs.into_iter().collect();
let mut registry = FunctionRegistry::new();
for (module_path, program) in programs {
for (module_path, program) in &programs {
for (name, signature) in program.function_registry.iter() {
let mut canonical_path = module_path.to_vec();
let mut canonical_path = (*module_path).to_vec();
canonical_path.push(name.clone());
registry.register_canonical_path(
&canonical_path,
Expand All @@ -216,6 +217,39 @@ impl<'a> IrCodegen<'a> {
);
}
}

let mut pending_reexports = Vec::new();
for (module_path, program) in &programs {
for reexport in &program.function_reexports {
let mut alias_path = (*module_path).to_vec();
alias_path.push(reexport.name.clone());
pending_reexports.push((alias_path, reexport.target_path.clone()));
}
}
while !pending_reexports.is_empty() {
let mut unresolved = Vec::new();
let mut made_progress = false;
for (alias_path, target_path) in pending_reexports {
if registry.get_canonical_path(&alias_path).is_some() {
made_progress = true;
continue;
}
if let Some(signature) = registry.get_canonical_path(&target_path).cloned() {
registry.register_canonical_path(
&alias_path,
signature.params.clone(),
signature.return_type.clone(),
);
made_progress = true;
} else {
unresolved.push((alias_path, target_path));
}
}
if !made_progress {
break;
}
pending_reexports = unresolved;
}
registry
}

Expand Down Expand Up @@ -573,34 +607,42 @@ impl<'a> IrCodegen<'a> {
callable_name_used_signature_keys_for_emit.extend(callable_name_use_facts.function_arg_signature_keys);
}

let mut canonical_registry = FunctionRegistry::new();
let mut dependency_ir_programs = Vec::new();
for (dep_name, dep_ast, dep_path_segments) in &self.dependency_modules {
// For dependencies, use best-effort lowering without type info to
// preserve prior behavior and avoid redundant typechecking.
let mut dep_lowering = AstLowering::new();
let dep_type_info = {
use crate::frontend::typechecker::TypeChecker;
let mut tc = TypeChecker::new();
self.configure_typechecker(&mut tc);
match tc.check_with_imports_allow_private(dep_ast, &deps) {
Ok(()) => tc.type_info().clone(),
Err(errs) => return Err(GenerationError::TypeCheck(errs)),
}
};
let mut dep_lowering = AstLowering::new_with_type_info(dep_type_info);
dep_lowering.set_current_source_module_name(
dep_ast
.source_path
.as_deref()
.and_then(crate::frontend::module::logical_module_name_from_source_path),
dep_path_segments
.clone()
.map(|segments| segments.join("."))
.or_else(|| {
dep_ast
.source_path
.as_deref()
.and_then(crate::frontend::module::logical_module_name_from_source_path)
}),
);
dep_lowering.seed_dependency_trait_decls(&self.dependency_modules);
dep_lowering.seed_struct_field_aliases(global_aliases.clone());
let dep_ir = dep_lowering.lower_program(dep_ast)?;
let module_path = dep_path_segments
.clone()
.unwrap_or_else(|| vec![(*dep_name).to_string()]);
for (name, signature) in dep_ir.function_registry.iter() {
let mut canonical_path = module_path.clone();
canonical_path.push(name.clone());
canonical_registry.register_canonical_path(
&canonical_path,
signature.params.clone(),
signature.return_type.clone(),
);
}
dependency_ir_programs.push(dep_ir);
dependency_ir_programs.push((module_path, dep_ir));
}
let canonical_registry = Self::canonical_registry_for_programs(
dependency_ir_programs
.iter()
.map(|(module_path, dep_ir)| (module_path.as_slice(), dep_ir)),
);

// Emit IR to Rust code
let use_emit_service = env::var("INCAN_EMIT_SERVICE").ok().as_deref() == Some("1");
Expand All @@ -625,7 +667,7 @@ impl<'a> IrCodegen<'a> {
inner.set_callable_name_resolutions(callable_name_resolutions_for_emit);
inner.set_callable_name_used_signature_keys(callable_name_used_signature_keys_for_emit);
inner.set_callable_name_local_registry(ir_program.function_registry.clone());
for dep_ir in &dependency_ir_programs {
for (_, dep_ir) in &dependency_ir_programs {
inner.seed_dependency_nominal_metadata_from_program(dep_ir);
}
Ok(svc.emit_program(&ir_program)?)
Expand All @@ -648,7 +690,7 @@ impl<'a> IrCodegen<'a> {
emitter.set_callable_name_resolutions(callable_name_resolutions_for_emit);
emitter.set_callable_name_used_signature_keys(callable_name_used_signature_keys_for_emit);
emitter.set_callable_name_local_registry(ir_program.function_registry.clone());
for dep_ir in &dependency_ir_programs {
for (_, dep_ir) in &dependency_ir_programs {
emitter.seed_dependency_nominal_metadata_from_program(dep_ir);
}
Ok(emitter.emit_program(&ir_program)?)
Expand Down
32 changes: 14 additions & 18 deletions src/backend/ir/emit/expressions/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ mod testing_asserts;
use proc_macro2::TokenStream;
use quote::quote;

use super::super::super::FunctionSignature;
use super::super::super::conversions::{BinOpEmitKind, determine_binop_plan};
use super::super::super::decl::FunctionParam;
use super::super::super::expr::{BinOp, IrCallArg, IrCallArgKind, IrExprKind, TypedExpr, VarRefKind};
use super::super::super::ownership::{ArgumentPassingPlan, ValueUseSite};
use super::super::super::types::IrType;
use super::super::super::{FunctionRegistry, FunctionSignature};
use super::super::{EmitError, IrEmitter};
use crate::frontend::ast::ParamKind;
use incan_core::lang::stdlib;
Expand Down Expand Up @@ -499,25 +499,21 @@ impl<'a> IrEmitter<'a> {
_ => None,
};
let callee_name = local_name.or(canonical_name);
let registry_signature = if let Some(path) = canonical_path {
self.canonical_function_registry().get_canonical_path(path)
} else {
local_name.and_then(|name| self.function_registry.get(name))
};
let result_specialized_signature = callable_signature.or(registry_signature).and_then(|signature| {
let merged_signature = FunctionRegistry::effective_call_signature_by(
self.function_registry,
self.canonical_function_registry(),
local_name,
canonical_path,
callable_signature,
Some(&func.ty),
|left, right| self.call_signature_type_matches(left, right),
);
let result_specialized_signature = merged_signature.as_ref().and_then(|signature| {
result_target_ty.and_then(|target_ty| Self::specialize_signature_by_result_target(signature, target_ty))
});
let function_sig = associated_signature.as_ref().or_else(|| {
if canonical_path.is_some() {
result_specialized_signature
.as_ref()
.or(callable_signature.or(registry_signature))
} else {
result_specialized_signature
.as_ref()
.or(registry_signature.or(callable_signature))
}
});
let function_sig = associated_signature
.as_ref()
.or_else(|| result_specialized_signature.as_ref().or(merged_signature.as_ref()));
// The checked-newtype lowering path emits a compiler-internal panic marker call. This remains the narrow,
// explicitly-tracked generated `panic!` exemption that issue #351 left to a separate follow-up. Render it as
// the Rust `panic!` macro so generated code stays valid without colliding with user-defined functions that may
Expand Down
20 changes: 7 additions & 13 deletions src/backend/ir/emit/expressions/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,17 +272,11 @@ impl<'a> IrEmitter<'a> {
.method_signature_for_receiver(&receiver.ty, method)
.or(specialized_signature.as_ref());
let has_incan_receiver_signature = receiver_signature.is_some();
let callable_signature = match (callable_signature, receiver_signature) {
(Some(call_sig), Some(method_sig))
if call_sig.params.iter().all(|param| param.default.is_none())
&& method_sig.params.iter().any(|param| param.default.is_some()) =>
{
Some(method_sig)
}
(Some(call_sig), _) => Some(call_sig),
(None, method_sig) => method_sig,
};
if let Some(sig) = callable_signature
let callable_signature =
FunctionSignature::merge_default_source_by(callable_signature, receiver_signature, |left, right| {
self.call_signature_type_matches(left, right)
});
if let Some(sig) = callable_signature.as_ref()
&& sig
.params
.iter()
Expand All @@ -291,7 +285,7 @@ impl<'a> IrEmitter<'a> {
return self.emit_rest_aware_call_args(receiver, args, sig);
}

let ordered_args: Vec<(TypedExpr, bool)> = if let Some(sig) = callable_signature {
let ordered_args: Vec<(TypedExpr, bool)> = if let Some(sig) = callable_signature.as_ref() {
if args.iter().any(|arg| arg.name.is_some()) {
let mut positional: Vec<TypedExpr> = Vec::new();
let mut named: std::collections::HashMap<&str, TypedExpr> = std::collections::HashMap::new();
Expand Down Expand Up @@ -335,7 +329,7 @@ impl<'a> IrEmitter<'a> {
.iter()
.enumerate()
.map(|(idx, (arg, from_default))| {
let param = callable_signature.and_then(|sig| sig.params.get(idx));
let param = callable_signature.as_ref().and_then(|sig| sig.params.get(idx));
let external_method_shape = matches!(
base_use_site,
ValueUseSite::ExternalCallArg { .. } | ValueUseSite::MethodArg
Expand Down
5 changes: 5 additions & 0 deletions src/backend/ir/emit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,11 @@ impl<'a> IrEmitter<'a> {
.unwrap_or(self.function_registry)
}

/// Return whether two call-signature types describe the same emitted surface after transparent aliases expand.
pub(in crate::backend::ir::emit) fn call_signature_type_matches(&self, left: &IrType, right: &IrType) -> bool {
left == right || self.resolve_type_aliases_for_emit(left) == self.resolve_type_aliases_for_emit(right)
}

/// Resolve transparent type aliases before emission decisions that need structural type information.
pub(in crate::backend::ir::emit) fn resolve_type_aliases_for_emit(&self, ty: &IrType) -> IrType {
let mut visiting = HashSet::new();
Expand Down
39 changes: 8 additions & 31 deletions src/backend/ir/emit/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -887,37 +887,14 @@ impl<'program> GeneratedUseAnalyzer<'program> {
IrExprKind::Var { name, .. } => Some(name.as_str()),
_ => None,
};
let canonical_name = canonical_path.as_ref().and_then(|path| path.last()).map(String::as_str);
let registered_signature = if canonical_path.is_some() {
callable_signature.cloned().or_else(|| {
canonical_path
.as_ref()
.and_then(|path| self.function_registry.get_canonical_path(path).cloned())
})
} else {
local_name
.and_then(|name| self.function_registry.get(name).cloned())
.or_else(|| canonical_name.and_then(|name| self.function_registry.get(name).cloned()))
.or_else(|| callable_signature.cloned())
};
registered_signature.or_else(|| match &func.ty {
IrType::Function { params, ret } => Some(FunctionSignature {
params: params
.iter()
.enumerate()
.map(|(idx, ty)| super::super::decl::FunctionParam {
name: format!("__incan_arg_{idx}"),
ty: ty.clone(),
mutability: super::super::types::Mutability::Immutable,
is_self: false,
kind: crate::frontend::ast::ParamKind::Normal,
default: None,
})
.collect(),
return_type: ret.as_ref().clone(),
}),
_ => None,
})
FunctionRegistry::effective_call_signature(
self.function_registry,
self.function_registry,
local_name,
canonical_path.as_deref(),
callable_signature,
Some(&func.ty),
)
}

/// Record named function arguments that need private adapters for borrowed function-pointer parameters.
Expand Down
Loading
Loading