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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
- Hardened MAST forest and package byte-slice deserialization against fuzzed length fields ([#3088](https://github.com/0xMiden/miden-vm/pull/3088)).
- [BREAKING] Bounded the live advice map by total field elements during execution; advice-provider setup now returns an error when initial advice exceeds this limit ([#3085](https://github.com/0xMiden/miden-vm/pull/3085)).
- Rejected empty kernel packages before linking so malformed dependency metadata returns a structured package error instead of reaching the linker's non-empty-kernel assertion ([#3082](https://github.com/0xMiden/miden-vm/pull/3082)).
- [BREAKING] Fixed project artifact reuse to ignore unrelated manifest fields, rejected private cross-module imports, and kept signature-only type imports live ([#3091](https://github.com/0xMiden/miden-vm/pull/3091)).

#### Changes

Expand Down
28 changes: 28 additions & 0 deletions crates/assembly-syntax/src/ast/item/resolver/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ pub enum SymbolResolutionError {
#[related]
actual: Option<RelatedLabel>,
},
#[error("private symbol reference")]
#[diagnostic(help("only public items can be referenced from another module"))]
PrivateSymbol {
#[label("this symbol is private to another module")]
span: SourceSpan,
#[source_code]
source_file: Option<Arc<SourceFile>>,
#[related]
defined: Option<RelatedLabel>,
},
#[error("type expression nesting depth exceeded")]
#[diagnostic(help("type expression nesting exceeded the maximum depth of {max_depth}"))]
TypeExpressionDepthExceeded {
Expand Down Expand Up @@ -158,6 +168,24 @@ impl SymbolResolutionError {
}
}

pub fn private_symbol(
span: SourceSpan,
defined: SourceSpan,
source_manager: &dyn SourceManager,
) -> Self {
let defined_source_file = source_manager.get(defined.source_id()).ok();
let source_file = source_manager.get(span.source_id()).ok();
Self::PrivateSymbol {
span,
source_file,
defined: Some(
RelatedLabel::advice("the referenced item is private")
.with_labeled_span(defined, "the referenced item is private")
.with_source_file(defined_source_file),
),
}
}

pub fn type_expression_depth_exceeded(
span: SourceSpan,
max_depth: usize,
Expand Down
10 changes: 10 additions & 0 deletions crates/assembly-syntax/src/ast/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@ pub fn visit_procedure<V, T>(visitor: &mut V, procedure: &Procedure) -> ControlF
where
V: ?Sized + Visit<T>,
{
if let Some(signature) = procedure.signature() {
for ty in signature.args.iter().chain(signature.results.iter()) {
visitor.visit_type_expr(ty)?;
}
}
visitor.visit_block(procedure.body())
}

Expand Down Expand Up @@ -894,6 +899,11 @@ pub fn visit_mut_procedure<V, T>(visitor: &mut V, procedure: &mut Procedure) ->
where
V: ?Sized + VisitMut<T>,
{
if let Some(signature) = procedure.signature_mut() {
for ty in signature.args.iter_mut().chain(signature.results.iter_mut()) {
visitor.visit_mut_type_expr(ty)?;
}
}
visitor.visit_mut_block(procedure.body_mut())
}

Expand Down
62 changes: 52 additions & 10 deletions crates/assembly/src/linker/resolver/symbol_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,16 @@ impl<'a> SymbolResolver<'a> {
context: &SymbolResolutionContext,
path: Span<&Path>,
ignored_imports: &mut BTreeSet<Arc<str>>,
) -> Result<SymbolResolution, LinkerError> {
self.expand_path_from(context.module, context, path, ignored_imports)
}

fn expand_path_from(
&self,
origin_module: ModuleIndex,
context: &SymbolResolutionContext,
path: Span<&Path>,
ignored_imports: &mut BTreeSet<Arc<str>>,
) -> Result<SymbolResolution, LinkerError> {
let span = path.span();
let mut path = path.into_inner();
Expand Down Expand Up @@ -386,19 +396,25 @@ impl<'a> SymbolResolver<'a> {
{
SymbolResolution::Local(item) => {
log::trace!(target: "name-resolver::expand", "resolved '{symbol}' to local symbol: {}", context.module + item.into_inner());
let gid = context.module + item.into_inner();
self.ensure_item_visible(origin_module, gid, span)?;
let path = self.module_path(context.module).join(&symbol);
Ok(SymbolResolution::Exact {
gid: context.module + item.into_inner(),
path: Span::new(span, path.into()),
})
Ok(SymbolResolution::Exact { gid, path: Span::new(span, path.into()) })
},
SymbolResolution::External(path) => {
log::trace!(target: "name-resolver::expand", "expanded '{symbol}' to unresolved external path '{path}'");
self.expand_path(&context, path.as_deref(), ignored_imports)
self.expand_path_from(
origin_module,
&context,
path.as_deref(),
ignored_imports,
)
},
resolved @ (SymbolResolution::MastRoot(_) | SymbolResolution::Exact { .. }) => {
resolved @ SymbolResolution::MastRoot(_) => Ok(resolved),
SymbolResolution::Exact { gid, path } => {
log::trace!(target: "name-resolver::expand", "resolved '{symbol}' to exact definition");
Ok(resolved)
self.ensure_item_visible(origin_module, gid, span)?;
Ok(SymbolResolution::Exact { gid, path })
},
SymbolResolution::Module { id, path: module_path } => {
log::trace!(target: "name-resolver::expand", "resolved '{symbol}' to module: id={id} path={module_path}");
Expand Down Expand Up @@ -437,7 +453,8 @@ impl<'a> SymbolResolver<'a> {
if ignored_imports.contains(imported_symbol) {
log::trace!(target: "name-resolver::expand", "skipping import expansion of '{imported_symbol}': already expanded, resolving as absolute path instead");
let path = path.to_absolute();
break self.expand_path(
break self.expand_path_from(
origin_module,
&context,
Span::new(span, path.as_ref()),
ignored_imports,
Expand Down Expand Up @@ -500,7 +517,8 @@ impl<'a> SymbolResolver<'a> {
let partially_expanded = external_path.join(subpath);
log::trace!(target: "name-resolver::expand", "partially expanded '{path}' to '{partially_expanded}'");
ignored_imports.insert(imported_symbol.to_string().into_boxed_str().into());
break self.expand_path(
break self.expand_path_from(
origin_module,
&context,
Span::new(span, partially_expanded.as_path()),
ignored_imports,
Expand All @@ -515,7 +533,8 @@ impl<'a> SymbolResolver<'a> {
// Try to expand the path by treating it as an absolute path
let absolute = path.to_absolute();
log::trace!(target: "name-resolver::expand", "no import found for '{imported_symbol}' in '{path}': attempting to resolve as absolute path instead");
break self.expand_path(
break self.expand_path_from(
origin_module,
&context,
Span::new(span, absolute.as_ref()),
ignored_imports,
Expand All @@ -530,6 +549,29 @@ impl<'a> SymbolResolver<'a> {
}
}

fn ensure_item_visible(
&self,
origin_module: ModuleIndex,
item: GlobalItemIndex,
span: SourceSpan,
) -> Result<(), LinkerError> {
if origin_module == item.module {
return Ok(());
}

let symbol = &self.graph[item.module][item.index];
if symbol.visibility().is_public() {
return Ok(());
}

Err(SymbolResolutionError::private_symbol(
span,
symbol.name().span(),
self.source_manager(),
)
.into())
}

pub fn resolve_local(
&self,
context: &SymbolResolutionContext,
Expand Down
1 change: 0 additions & 1 deletion crates/assembly/src/linker/rewrites/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ impl<'a, 'b: 'a> VisitMut<LinkerError> for ModuleRewriter<'a, 'b> {
fn visit_mut_alias(&mut self, alias: &mut Alias) -> ControlFlow<LinkerError> {
match alias.target() {
AliasTarget::MastRoot(_) => return ControlFlow::Continue(()),
AliasTarget::Path(path) if path.is_absolute() => return ControlFlow::Continue(()),
AliasTarget::Path(_) => (),
}
log::debug!(target: "linker", " * rewriting alias target {}", alias.target());
Expand Down
8 changes: 4 additions & 4 deletions crates/assembly/src/project/build_provenance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ use super::PackageBuildSettings;
///
/// The recorded fields differ by source origin:
///
/// - [`Self::Path`] tracks a hash of the effective manifest and the target's resolved source files,
/// along with a hash of the fully resolved dependency closure and the build-profile knobs that
/// affect the emitted package bytes.
/// - [`Self::Path`] tracks a hash of the selected target's build-provenance projection and resolved
/// source files, along with a hash of the fully resolved dependency closure and the build-profile
/// knobs that affect the emitted package bytes.
/// - [`Self::Git`] records the repository identity and resolved revision instead of hashing the
/// checked-out source tree directly, but still includes the dependency-closure hash and build
/// settings for the same reuse decision.
Expand All @@ -46,7 +46,7 @@ use super::PackageBuildSettings;
pub(super) enum PackageBuildProvenance {
/// Provenance for a package assembled from sources addressed by a local filesystem path.
Path {
/// Hash of the effective manifest plus the root/support modules that define the target.
/// Hash of the build-provenance projection plus the root/support modules for the target.
source_hash: Word,
/// Hash of the resolved dependency closure, including linkage and exact selected
/// artifacts.
Expand Down
8 changes: 4 additions & 4 deletions crates/assembly/src/project/dependency_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,13 @@ impl DependencyGraph {
profile_name: &str,
origin: &ProjectSourceOrigin,
manifest_path: &FsPath,
workspace_root: Option<&FsPath>,
_workspace_root: Option<&FsPath>,
visiting: &mut BTreeSet<PackageId>,
) -> Result<PackageBuildProvenance, Report> {
let dependency_hash =
self.compute_dependency_closure_hash(package_id, profile_name, visiting)?;
let build_settings =
PackageBuildSettings::from_profile(project.resolve_profile(profile_name)?);
let profile = project.resolve_profile(profile_name)?;
let build_settings = PackageBuildSettings::from_profile(profile);

match origin {
ProjectSourceOrigin::Git { repo, resolved_revision, .. } => {
Expand All @@ -172,8 +172,8 @@ impl DependencyGraph {
Ok(PackageBuildProvenance::Path {
source_hash: project.compute_path_source_hash(
target,
profile,
manifest_path,
workspace_root,
)?,
dependency_hash,
build_settings,
Expand Down
66 changes: 4 additions & 62 deletions crates/assembly/src/project/package_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use miden_assembly_syntax::{
};
use miden_core::{Word, utils::hash_string_to_word};
use miden_package_registry::PackageId;
use miden_project::{DependencyVersionScheme, Package as ProjectPackage, Profile, Target};
use miden_project::{Package as ProjectPackage, Profile, Target};

use super::TargetSourcePaths;
use crate::SourceManager;
Expand All @@ -39,8 +39,8 @@ pub(super) trait ProjectPackageExt {
fn compute_path_source_hash(
&self,
target: &Target,
profile: &Profile,
manifest_path: &FsPath,
workspace_root: Option<&FsPath>,
) -> Result<Word, Report>;

fn excluded_target_roots(
Expand All @@ -51,8 +51,6 @@ pub(super) trait ProjectPackageExt {

fn resolve_target_source_paths(&self, target: &Target) -> Result<TargetSourcePaths, Report>;

fn effective_manifest_hash_input(&self) -> Result<String, Report>;

fn resolve_profile(&self, name: &str) -> Result<&Profile, Report>;
}

Expand Down Expand Up @@ -125,8 +123,8 @@ impl ProjectPackageExt for ProjectPackage {
fn compute_path_source_hash(
&self,
target: &Target,
profile: &Profile,
manifest_path: &FsPath,
workspace_root: Option<&FsPath>,
) -> Result<Word, Report> {
let source_paths = self.resolve_target_source_paths(target)?;
let project_root = manifest_path.parent().ok_or_else(|| {
Expand All @@ -152,24 +150,7 @@ impl ProjectPackageExt for ProjectPackage {
}
inputs.sort_by(|a, b| a.0.cmp(&b.0));

let mut material = format!(
"target:{}\nkind:{}\nnamespace:{}\n",
target.name.inner(),
target.ty,
target.namespace.inner()
);
if workspace_root.is_some() {
material.push_str("manifest:effective\n");
material.push_str(&self.effective_manifest_hash_input()?);
material.push('\n');
} else {
let manifest_label = manifest_path
.strip_prefix(project_root)
.unwrap_or(manifest_path)
.display()
.to_string();
inputs.push((format!("manifest:{manifest_label}"), manifest_path.to_path_buf()));
}
let mut material = self.build_provenance_projection(target, profile);
for (label, path) in inputs {
let bytes = fs::read(&path).map_err(|error| {
Report::msg(format!("failed to read source input '{}': {error}", path.display()))
Expand Down Expand Up @@ -212,45 +193,6 @@ impl ProjectPackageExt for ProjectPackage {
Ok(TargetSourcePaths { root: root_path, root_dir, support })
}

fn effective_manifest_hash_input(&self) -> Result<String, Report> {
let mut manifest = self.to_toml()?;

let mut workspace_dependencies = self
.dependencies()
.iter()
.filter_map(|dependency| match dependency.scheme() {
DependencyVersionScheme::Workspace { member, version } => Some((
dependency.name().to_string(),
member.path().to_string(),
version.as_ref().map(ToString::to_string),
dependency.linkage(),
)),
DependencyVersionScheme::WorkspacePath { path, version } => Some((
dependency.name().to_string(),
path.path().to_string(),
version.as_ref().map(ToString::to_string),
dependency.linkage(),
)),
_ => None,
})
.collect::<Vec<_>>();
workspace_dependencies.sort_by(|a, b| a.0.cmp(&b.0));

if !workspace_dependencies.is_empty() {
manifest.push_str("\n# resolved_workspace_dependencies\n");
for (name, member_path, version, linkage) in workspace_dependencies {
match version {
Some(version) => {
manifest.push_str(&format!("{name}={member_path}@{version}:{linkage}\n"));
},
None => manifest.push_str(&format!("{name}={member_path}:{linkage}\n")),
}
}
}

Ok(manifest)
}

fn resolve_profile(&self, name: &str) -> Result<&Profile, Report> {
self.get_profile(name).ok_or_else(|| {
Report::msg(format!(
Expand Down
Loading
Loading