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 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 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
32 changes: 28 additions & 4 deletions crates/sema/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ pub use parse::{ParsedSource, ParsedSources, ParsingContext};

pub mod builtins;
pub mod eval;

pub mod hir;
pub use hir::{Arena, Hir};

pub mod ty;

mod typeck;
Expand All @@ -40,6 +43,17 @@ pub mod stats;

/// Parses and semantically analyzes all the loaded sources, recursing into imports.
pub fn parse_and_resolve(pcx: ParsingContext<'_>) -> Result<()> {
let _ = parse_and_lower(pcx, None)?;

Ok(())
}

/// Parses and lowers to HIR, recursing into imports. If called with an external HIR arena then
/// returns the global compilation context (which can be used to access HIR).
pub fn parse_and_lower<'hir>(
pcx: ParsingContext<'hir>,
hir_arena: Option<&'hir ThreadLocal<Arena>>,
) -> Result<Option<Gcx<'hir>>> {
let sess = pcx.sess;

if pcx.sources.is_empty() {
Expand All @@ -53,6 +67,17 @@ pub fn parse_and_resolve(pcx: ParsingContext<'_>) -> Result<()> {
debug_span!("dropping_ast_arenas").in_scope(|| drop(arenas));
});
let mut sources = pcx.parse(&ast_arenas);
sources.topo_sort();

if let Some(hir_arena) = hir_arena {
let (hir, symbol_resolver) = lower(sess, &sources, hir_arena.get_or_default())?;
let global_context =
OnDrop::new(ty::GlobalCtxt::new(sess, hir_arena, hir, symbol_resolver), |gcx| {
debug_span!("drop_gcx").in_scope(|| drop(gcx));
});
let gcx = ty::Gcx::new(unsafe { trustme::decouple_lt(&global_context) });
return Ok(Some(gcx));
}

if let Some(dump) = &sess.opts.unstable.dump {
if dump.kind.is_ast() {
Expand All @@ -67,15 +92,14 @@ pub fn parse_and_resolve(pcx: ParsingContext<'_>) -> Result<()> {
}

if sess.opts.language.is_yul() || sess.stop_after(CompilerStage::Parsed) {
return Ok(());
return Ok(None);
}

sources.topo_sort();

let hir_arena = OnDrop::new(ThreadLocal::<hir::Arena>::new(), |hir_arena| {
debug!(hir_allocated = hir_arena.get_or_default().allocated_bytes());
debug_span!("dropping_hir_arena").in_scope(|| drop(hir_arena));
});

let (hir, symbol_resolver) = lower(sess, &sources, hir_arena.get_or_default())?;

// Drop the ASTs and AST arenas in a separate thread.
Expand All @@ -97,7 +121,7 @@ pub fn parse_and_resolve(pcx: ParsingContext<'_>) -> Result<()> {
let gcx = ty::Gcx::new(unsafe { trustme::decouple_lt(&global_context) });
analysis(gcx)?;

Ok(())
Ok(None)
}

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

pub fn parse_and_lower(
self,
hir_arena: &'sess ThreadLocal<Arena>,
) -> Result<Option<Gcx<'sess>>> {
crate::parse_and_lower(self, Some(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
5 changes: 5 additions & 0 deletions crates/sema/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ impl<'gcx> Gcx<'gcx> {
self.interner.arena.get_or_default()
}

/// Returns the HIR.
pub fn hir(self) -> &'gcx Hir<'gcx> {
&self.hir
}

pub fn bump(self) -> &'gcx bumpalo::Bump {
&self.arena().bump
}
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();
}
}
43 changes: 43 additions & 0 deletions examples/src/hir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use solar::{
interface::{diagnostics::EmittedDiagnostics, Session},
sema::{
hir::{Arena, ContractId},
thread_local::ThreadLocal,
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 = ThreadLocal::<Arena>::new();
let mut parsing_context = ParsingContext::new(&sess);
parsing_context.load_files(paths)?;

if let Some(gcx) = parsing_context.parse_and_lower(&hir_arena)? {
for contract in gcx.hir().contracts() {
println!("contract: {}", contract.name);
}
let counter_contract = gcx.hir().contract(ContractId::new(0));
assert_eq!(counter_contract.name.to_string(), "Counter");
let another_counter_contract = gcx.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