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
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ Solidity → Slang (parse, bind) → Sol-dialect MLIR → Sol→Yul→Standard p
| [`solx-compiler-downloader`](./solx-compiler-downloader/) | Downloads and verifies compiler binaries |
| [`solx-benchmark-converter`](./solx-benchmark-converter/) | Benchmark analysis and comparison |

## Code layout

A type has one inherent `impl` block, with public items before private ones.

## Slang Frontend

`solx-slang` lowers the Slang AST to Sol-dialect MLIR through `solx-mlir`. The rules below are the design law of that frontend and the conventions a reviewer would otherwise repeat by hand.
Expand Down
18 changes: 17 additions & 1 deletion docs/src/user-guide/02-command-line-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,20 @@ The options in this section configure the **solx** compilation pipeline.



### `--solidity-version`

Sets the Solidity version the sources are compiled as. Slang accepts the syntax and built-ins available in that version, and every `pragma solidity` must allow it. The version is also recorded in the metadata.

Versions from 0.8.0 up to the latest one Slang supports are accepted, and the latest is the default.

Usage:

```bash
solx 'Simple.sol' --bin --solidity-version '0.8.20'
```



### `--threads`

Sets the number of threads used for parallel compilation. Each thread compiles a separate translation unit in a child process. By default, the number of threads equals the number of CPU cores.
Expand Down Expand Up @@ -637,7 +651,9 @@ Only the following EVM versions are supported:

- cancun
- prague
- osaka (default)
- osaka

The default is solc's default for the Solidity version, which is osaka for the latest one. A Solidity version older than 0.8.25 defaults to an EVM version **solx** does not support yet, so it is compiled for cancun with a warning.

Usage:

Expand Down
8 changes: 7 additions & 1 deletion docs/src/user-guide/03-standard-json.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,14 @@ Optimizer modes follow the behavior described in [Optimizer and Assembly Semanti
// Supported EVM versions: "cancun", "prague", "osaka".
// For instance, with version "osaka", solx will be producing `clz` instructions, whereas for older EVM versions it will not.
// The oldest supported EVM version is "cancun".
// Default: "osaka".
// Default: solc's default for "solidityVersion", which is "osaka" for the latest one.
// A Solidity version older than "0.8.25" defaults to an EVM version solx does not support yet, so it is compiled for "cancun" with a warning.
"evmVersion": "osaka",
// Optional, solx-only: Solidity version the sources are compiled as.
// Slang accepts the syntax and built-ins available in that version, and every `pragma solidity` must allow it.
// Supported versions: from "0.8.0" up to the latest one Slang supports.
// Default: the latest version Slang supports.
"solidityVersion": "0.8.37",
// Optional: Select the desired output.
// Default: no flags are selected, and no output is generated.
"outputSelection": {
Expand Down
15 changes: 15 additions & 0 deletions solx-core/src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ pub struct Arguments {
#[arg(long, help_heading = "Compilation Settings")]
pub evm_version: Option<solx_utils::EVMVersion>,

/// Solidity language version to compile as, from 0.8.0 up.
/// The default is the latest version the frontend supports.
#[arg(long, value_name = "VERSION", help_heading = "Compilation Settings")]
pub solidity_version: Option<semver::Version>,

/// Accepted for compatibility and ignored with a warning.
#[arg(long, help_heading = "Compilation Settings")]
pub via_ir: bool,
Expand Down Expand Up @@ -316,6 +321,11 @@ impl Arguments {
"EVM version is only allowed in Solidity mode.",
));
}
if self.solidity_version.is_some() {
messages.push(solx_standard_json::OutputError::new_error(
"Solidity version is only allowed in Solidity mode.",
));
}

if self.via_ir {
messages.push(solx_standard_json::OutputError::new_error(
Expand Down Expand Up @@ -368,6 +378,11 @@ impl Arguments {
"EVM version must be passed via standard JSON input.",
));
}
if self.solidity_version.is_some() {
messages.push(solx_standard_json::OutputError::new_error(
"Solidity version must be passed via standard JSON input.",
));
}

if self.output_dir.is_some() {
messages.push(solx_standard_json::OutputError::new_error(
Expand Down
15 changes: 13 additions & 2 deletions solx-core/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ impl<'arguments> Compiler<'arguments> {
&output_selection,
messages,
self.arguments.evm_version,
self.arguments.solidity_version.clone(),
self.arguments.via_ir,
metadata_hash_type,
self.arguments.metadata_literal,
Expand Down Expand Up @@ -218,6 +219,7 @@ impl<'arguments> Compiler<'arguments> {
output_selection: &solx_standard_json::InputSelection,
messages: Arc<Mutex<Vec<solx_standard_json::OutputError>>>,
evm_version: Option<solx_utils::EVMVersion>,
solidity_version: Option<semver::Version>,
via_ir: bool,
metadata_hash_type: solx_utils::MetadataHashType,
metadata_literal: bool,
Expand All @@ -236,6 +238,7 @@ impl<'arguments> Compiler<'arguments> {
remappings,
solx_standard_json::InputOptimizer::default(),
evm_version,
solidity_version,
via_ir,
output_selection,
solx_standard_json::InputMetadata::new(
Expand All @@ -257,7 +260,11 @@ impl<'arguments> Compiler<'arguments> {

let run_solx_project = profiler.start_pipeline_element("solx_BuildProject");
let project = Project::try_from_solidity_output(
&frontend.version,
input
.settings
.solidity_version
.clone()
.expect("the frontend sets the version it compiled as"),
input.settings.libraries.clone(),
&mut output,
)?;
Expand Down Expand Up @@ -338,7 +345,11 @@ impl<'arguments> Compiler<'arguments> {

let run_solx_project = profiler.start_pipeline_element("solx_BuildProject");
let project = Project::try_from_solidity_output(
&frontend.version,
input
.settings
.solidity_version
.clone()
.expect("the frontend sets the version it compiled as"),
input.settings.libraries.clone(),
&mut output,
)?;
Expand Down
4 changes: 2 additions & 2 deletions solx-core/src/project/contract/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ impl<'a> Metadata<'a> {
/// A shortcut constructor.
///
pub fn new(
solc_version: Option<&solx_standard_json::Version>,
solc_version: Option<&semver::Version>,
optimizer_settings: solx_codegen_evm::OptimizerSettings,
llvm_options: &'a [String],
) -> Self {
Self {
solc_version: solc_version.map(|version| version.default.to_owned()),
solc_version: solc_version.cloned(),
solx_version: crate::Compiler::version()
.parse()
.expect("version string is valid semver"),
Expand Down
12 changes: 6 additions & 6 deletions solx-core/src/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use self::contract::metadata::Metadata as ContractMetadata;
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Project {
/// The `solc` compiler version, absent for LLVM IR projects.
pub solc_version: Option<solx_standard_json::Version>,
pub solc_version: Option<semver::Version>,
/// The project build results.
pub contracts: BTreeMap<String, Contract>,
/// The Solidity AST JSONs of the source files.
Expand All @@ -51,7 +51,7 @@ impl Project {
/// A shortcut constructor.
///
pub fn new(
solc_version: Option<solx_standard_json::Version>,
solc_version: Option<semver::Version>,
contracts: BTreeMap<String, Contract>,
ast_jsons: Option<BTreeMap<String, Option<Box<RawValue>>>>,
libraries: solx_utils::Libraries,
Expand All @@ -68,7 +68,7 @@ impl Project {
/// Parses the Solidity `sources` and returns a Solidity project.
///
pub fn try_from_solidity_output(
solc_version: &solx_standard_json::Version,
solc_version: semver::Version,
libraries: solx_utils::Libraries,
output: &mut solx_standard_json::Output,
) -> anyhow::Result<Self> {
Expand Down Expand Up @@ -148,7 +148,7 @@ impl Project {
}
}
Ok(Project::new(
Some(solc_version.to_owned()),
Some(solc_version),
contracts,
Some(ast_jsons),
libraries,
Expand Down Expand Up @@ -425,7 +425,7 @@ impl Project {
///
fn cbor_metadata(
metadata: Option<&str>,
solc_version: Option<&solx_standard_json::Version>,
solc_version: Option<&semver::Version>,
metadata_hash_type: solx_utils::MetadataHashType,
append_cbor: bool,
) -> Option<Vec<u8>> {
Expand All @@ -450,7 +450,7 @@ impl Project {
if let Some(solc_version) = solc_version {
cbor_version_parts.push((
crate::r#const::SOLC_METADATA_TAG.to_owned(),
solc_version.default.to_owned(),
solc_version.to_owned(),
));
}
let cbor_data = (
Expand Down
121 changes: 95 additions & 26 deletions solx-slang/src/slang/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use slang_solidity_v2::compilation::Configuration;
use slang_solidity_v2::compilation::FileId;
use slang_solidity_v2::diagnostics::DiagnosticExtensions;
use slang_solidity_v2::diagnostics::DiagnosticSeverity;
use slang_solidity_v2::utils::EvmTarget;
use slang_solidity_v2::utils::LanguageVersion;

use solx_standard_json::CollectableError;
Expand Down Expand Up @@ -47,31 +48,14 @@ impl Slang {
/// The frontend name the compiler reports and prefixes its pipeline benchmarks with.
pub const NAME: &'static str = "Slang";

/// Builds a Slang compilation unit from the given source files, parsing every source and
/// resolving imports with the given remappings, following solc's semantics.
fn compile(
language_version: LanguageVersion,
evm_version: EVMVersion,
sources: &BTreeMap<FileId, &str>,
remappings: &[Remapping],
) -> CompilationUnit {
CompilationUnit::create(Configuration {
language_version,
evm_target: evm_version.into(),
sources: sources
.iter()
.map(|(file_id, content)| (file_id.clone(), *content)),
resolver: SourceImportResolver { remappings },
})
}
}

impl Slang {
///
/// The Solidity `--standard-json` mirror.
///
/// Metadata is always requested in order to calculate the metadata hash even if not requested in the `output_selection`.
///
/// Sets `settings.solidity_version` to the version the sources are compiled as, which goes into
/// the metadata.
///
pub fn standard_json(
&self,
input_json: &mut solx_standard_json::Input,
Expand Down Expand Up @@ -116,12 +100,22 @@ impl Slang {
sources.insert(path.as_str().into(), source_code);
}

let language_version = LanguageVersion::try_from(self.version.default.clone())
.expect("the frontend version is a Slang language version");
let evm_version = input_json.settings.evm_version.unwrap_or_else(|| {
EVMVersion::try_from(language_version.default_evm_target())
.expect("solx supports the default EVM version of its language version")
});
let language_version = match Self::language_version(
input_json.settings.solidity_version.as_ref(),
&mut output.errors,
) {
Ok(language_version) => language_version,
Err(error) => {
output.errors.push(OutputError::new_error(error));
return Ok(output);
}
};
input_json.settings.solidity_version = Some(language_version.into());
let evm_version = Self::evm_version(
input_json.settings.evm_version,
language_version,
&mut output.errors,
);

let run_analysis =
profiler.start_pipeline_element(format!("{}_ParseAndBind", Self::NAME).as_str());
Expand Down Expand Up @@ -215,4 +209,79 @@ impl Slang {

Ok(output)
}

/// Picks the language version to compile as: the requested one, or the latest Slang supports.
///
/// A prerelease or build suffix is dropped with a warning pushed to `messages`.
///
/// # Errors
///
/// Returns an error if Slang does not support the requested version.
fn language_version(
requested: Option<&semver::Version>,
messages: &mut Vec<OutputError>,
) -> Result<LanguageVersion, String> {
let Some(requested) = requested else {
return Ok(LanguageVersion::LATEST);
};
let mut version = requested.clone();
if !version.pre.is_empty() || !version.build.is_empty() {
version.pre = semver::Prerelease::EMPTY;
version.build = semver::BuildMetadata::EMPTY;
messages.push(OutputError::new_warning(format!(
"Solidity version {requested} is compiled as {version}: the suffix names a solc build, which does not change the language version."
)));
}
version.clone().try_into().map_err(|_| {
format!(
"Solidity version {version} is not supported. Supported versions are {} to {}.",
semver::Version::from(LanguageVersion::EARLIEST),
semver::Version::from(LanguageVersion::LATEST),
)
})
}

/// Picks the EVM version to analyze at: the requested one, or solc's default for the language
/// version.
///
/// A default older than Cancun falls back to Cancun with a warning pushed to `messages`.
fn evm_version(
requested: Option<EVMVersion>,
language_version: LanguageVersion,
messages: &mut Vec<OutputError>,
) -> EVMVersion {
if let Some(requested) = requested {
return requested;
}
let default = language_version.default_evm_target();
if default < EvmTarget::Cancun {
// TODO: target the EVM versions older than Cancun that solc defaults to.
messages.push(OutputError::new_warning(format!(
"Solidity version {} defaults to EVM version {}, which solx does not support yet. Compiling for {}, the oldest EVM version solx supports.",
semver::Version::from(language_version),
default.to_string().to_lowercase(),
EVMVersion::Cancun,
)));
return EVMVersion::Cancun;
}
EVMVersion::try_from(default).expect("solx supports every EVM version from Cancun up")
}

/// Builds a Slang compilation unit from the given source files, parsing every source and
/// resolving imports with the given remappings, following solc's semantics.
fn compile(
language_version: LanguageVersion,
evm_version: EVMVersion,
sources: &BTreeMap<FileId, &str>,
remappings: &[Remapping],
) -> CompilationUnit {
CompilationUnit::create(Configuration {
language_version,
evm_target: evm_version.into(),
sources: sources
.iter()
.map(|(file_id, content)| (file_id.clone(), *content)),
resolver: SourceImportResolver { remappings },
})
}
}
4 changes: 4 additions & 0 deletions solx-standard-json/src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ impl Input {
remappings: Vec<solx_utils::Remapping>,
optimizer: InputSettingsOptimizer,
evm_version: Option<solx_utils::EVMVersion>,
solidity_version: Option<semver::Version>,
via_ir: bool,
output_selection: &InputSettingsSelection,
metadata: InputSettingsMetadata,
Expand Down Expand Up @@ -104,6 +105,7 @@ impl Input {
remappings,
optimizer,
evm_version,
solidity_version,
via_ir,
output_selection,
metadata,
Expand All @@ -122,6 +124,7 @@ impl Input {
remappings: Vec<solx_utils::Remapping>,
optimizer: InputSettingsOptimizer,
evm_version: Option<solx_utils::EVMVersion>,
solidity_version: Option<semver::Version>,
via_ir: bool,
output_selection: &InputSettingsSelection,
metadata: InputSettingsMetadata,
Expand All @@ -136,6 +139,7 @@ impl Input {
libraries,
remappings,
evm_version,
solidity_version,
via_ir,
output_selection.to_owned(),
metadata,
Expand Down
Loading
Loading