Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hir): expose hir from parsing context #210

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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 CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ to ignore benchmarks and examples:
```
cargo check --workspace
cargo clippy --workspace
cargo fmt --all --check
cargo test --workspace
```

Expand Down
27 changes: 27 additions & 0 deletions crates/sema/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod ast_lowering;
mod ast_passes;

mod parse;
use crate::hir::{Arena, Hir};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should maintain style of the other fns, using hir:: as a module instead of importing

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed in 9c654cc

pub use parse::{ParsedSource, ParsedSources, ParsingContext};

pub mod builtins;
Expand Down Expand Up @@ -100,6 +101,32 @@ pub fn parse_and_resolve(pcx: ParsingContext<'_>) -> Result<()> {
Ok(())
}

/// Parses and lowers to HIR, recursing into imports.
pub fn parse_and_lower_to_hir<'hir>(
Copy link
Member

@DaniPopes DaniPopes Mar 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to avoid any code dup here and also avoid using Hir directly, but instead through gcx; I think what we can do here is something like this

fn parse_and_resolve() {
    let gcx = *parse_and_lower()?;
    analysis(gcx)?;
    Ok(())
}

fn parse_and_lower<'hir>(pcx: '_, hir_arena: 'hir) -> impl Deref<Target=Gcx<'hir>> {
    // all the code currently in parse_and_resolve ...
    GcxWrapper(...)
}

struct GcxWrapper(GlobalContext);
impl Deref for GcxWrapper {
    // move the unsafe here
}
impl Drop for GcxWrapper {
    // move from OnDrop closure
}

This way we can keep the drop impl and avoid a closure for using Gcx

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DaniPopes would the approach in 9c654cc which doesn't introduce a new wrapper work?

pcx: ParsingContext<'_>,
hir_arena: &'hir Arena,
) -> Result<Hir<'hir>> {
let sess = pcx.sess;

if pcx.sources.is_empty() {
let msg = "no files found";
let note = "if you wish to use the standard input, please specify `-` explicitly";
return Err(sess.dcx.err(msg).note(note).emit());
}

let ast_arenas = OnDrop::new(ThreadLocal::<ast::Arena>::new(), |mut arenas| {
debug!(asts_allocated = arenas.iter_mut().map(|a| a.allocated_bytes()).sum::<usize>());
debug_span!("dropping_ast_arenas").in_scope(|| drop(arenas));
});
let mut sources = pcx.parse(&ast_arenas);

sources.topo_sort();

let (hir, _) = lower(sess, &sources, hir_arena)?;

Ok(hir)
}

/// Lowers the parsed ASTs into the HIR.
fn lower<'sess, 'hir>(
sess: &'sess Session,
Expand Down
6 changes: 5 additions & 1 deletion crates/sema/src/parse.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::hir::SourceId;
use crate::hir::{Arena, Hir, SourceId};
use rayon::prelude::*;
use solar_ast as ast;
use solar_data_structures::{
Expand Down Expand Up @@ -90,6 +90,10 @@ impl<'sess> ParsingContext<'sess> {
crate::parse_and_resolve(self)
}

pub fn parse_and_lower_to_hir(self, hir_arena: &Arena) -> Result<Hir<'_>> {
crate::parse_and_lower_to_hir(self, hir_arena)
}

/// Parses all the loaded sources, recursing into imports.
///
/// Sources are not guaranteed to be in any particular order, as they may be parsed in parallel.
Expand Down
20 changes: 20 additions & 0 deletions examples/src/AnotherCounter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "./Counter.sol";

contract AnotherCounter {
Counter counter = new Counter();

constructor(Counter _counter) {
counter = _counter;
}

function setNumber(uint256 newNumber) public {
counter.setNumber(newNumber);
}

function increment() public {
counter.increment();
}
}
36 changes: 36 additions & 0 deletions examples/src/hir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use solar::{
interface::{diagnostics::EmittedDiagnostics, Session},
sema::{
hir::{Arena, ContractId},
ParsingContext,
},
};
use std::path::Path;

#[test]
fn main() -> Result<(), EmittedDiagnostics> {
let paths = vec![Path::new("src/AnotherCounter.sol")];

// Create a new session with a buffer emitter.
// This is required to capture the emitted diagnostics and to return them at the end.
let sess = Session::builder().with_buffer_emitter(solar::interface::ColorChoice::Auto).build();

// Enter the context and parse the file.
// Counter will be parsed, even if not explicitly provided, since it is a dependency.
let _ = sess.enter_parallel(|| -> solar::interface::Result<()> {
// Set up the parser.
let hir_arena = Arena::new();
let mut parsing_context = ParsingContext::new(&sess);
parsing_context.load_files(paths)?;
let hir = parsing_context.parse_and_lower_to_hir(&hir_arena)?;
let counter_contract = hir.contract(ContractId::new(0));
assert_eq!(counter_contract.name.to_string(), "Counter");
let another_counter_contract = hir.contract(ContractId::new(1));
assert_eq!(another_counter_contract.name.to_string(), "AnotherCounter");
Ok(())
});

// Return the emitted diagnostics as a `Result<(), _>`.
// If any errors were emitted, this returns `Err(_)`, otherwise `Ok(())`.
sess.emitted_errors().unwrap()
}
1 change: 1 addition & 0 deletions examples/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
#![allow(unreachable_pub)]
#![cfg(test)]

mod hir;
mod parser;
Loading