Skip to content

Commit 646e4fd

Browse files
committed
feat(hints): support minimum optimization levels
RFC 3924 lets a package request a numeric optimization floor without overriding an application's profile choices. Apply the hint after profile and built-in host defaults but before explicit package and build overrides, while leaving the unordered size levels unchanged. Signed-off-by: 0xPoe <poe.liu@pm.me>
1 parent 4b536a8 commit 646e4fd

17 files changed

Lines changed: 254 additions & 59 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ cargo-platform = { path = "crates/cargo-platform", version = "0.3.3" }
3636
cargo-test-macro = { version = "0.4.15", path = "crates/cargo-test-macro" }
3737
cargo-test-support = { version = "0.11.4", path = "crates/cargo-test-support" }
3838
cargo-util = { version = "0.2.33", path = "crates/cargo-util" }
39-
cargo-util-schemas = { version = "0.14.4", path = "crates/cargo-util-schemas" }
39+
cargo-util-schemas = { version = "0.15.0", path = "crates/cargo-util-schemas" }
4040
cargo-util-terminal = { version = "0.1.3", path = "crates/cargo-util-terminal" }
4141
cargo_metadata = "0.23.1"
4242
clap = "4.6.0"

crates/cargo-util-schemas/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cargo-util-schemas"
3-
version = "0.14.4"
3+
version = "0.15.0"
44
rust-version = "1.98" # MSRV:1
55
edition.workspace = true
66
license.workspace = true

crates/cargo-util-schemas/manifest.schema.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,16 @@
10301030
"Hints": {
10311031
"type": "object",
10321032
"properties": {
1033+
"min-opt-level": {
1034+
"anyOf": [
1035+
{
1036+
"$ref": "#/$defs/TomlValue"
1037+
},
1038+
{
1039+
"type": "null"
1040+
}
1041+
]
1042+
},
10331043
"mostly-unused": {
10341044
"anyOf": [
10351045
{
@@ -1444,7 +1454,11 @@
14441454
"type": "string"
14451455
},
14461456
"TomlDebugInfo": {
1447-
"type": ["string", "integer", "boolean"],
1457+
"type": [
1458+
"string",
1459+
"integer",
1460+
"boolean"
1461+
],
14481462
"enum": [
14491463
"none",
14501464
"line-directives-only",

crates/cargo-util-schemas/src/manifest/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,11 @@ pub enum TomlLintLevel {
17051705
#[serde(rename_all = "kebab-case")]
17061706
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
17071707
pub struct Hints {
1708+
#[cfg_attr(
1709+
feature = "unstable-schema",
1710+
schemars(with = "Option<TomlValueWrapper>")
1711+
)]
1712+
pub min_opt_level: Option<toml::Value>,
17081713
#[cfg_attr(
17091714
feature = "unstable-schema",
17101715
schemars(with = "Option<TomlValueWrapper>")

src/compiler/standard_lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ pub fn generate_std_roots(
127127
interner: &UnitInterner,
128128
profiles: &Profiles,
129129
target_data: &RustcTargetData<'_>,
130+
hint_min_opt_level: bool,
130131
) -> CargoResult<HashMap<CompileKind, Vec<Unit>>> {
131132
// Generate a map of Units for each kind requested.
132133
let mut ret = HashMap::default();
@@ -149,6 +150,7 @@ pub fn generate_std_roots(
149150
interner,
150151
profiles,
151152
target_data,
153+
hint_min_opt_level,
152154
)?;
153155
}
154156

@@ -167,6 +169,7 @@ fn generate_roots(
167169
interner: &UnitInterner,
168170
profiles: &Profiles,
169171
target_data: &RustcTargetData<'_>,
172+
hint_min_opt_level: bool,
170173
) -> CargoResult<()> {
171174
let std_ids = std_crates(crates, default, units)
172175
.iter()
@@ -191,6 +194,8 @@ fn generate_roots(
191194
let unit_for = UnitFor::new_normal(kind);
192195
let profile = profiles.get_profile(
193196
pkg.package_id(),
197+
pkg.hints(),
198+
hint_min_opt_level,
194199
/*is_member*/ false,
195200
/*is_local*/ false,
196201
unit_for,

src/compiler/unit_dependencies.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,8 @@ fn new_unit_dep(
867867
let is_local = pkg.package_id().source_id().is_path() && !state.is_std;
868868
let profile = state.profiles.get_profile(
869869
pkg.package_id(),
870+
pkg.hints(),
871+
state.gctx.cli_unstable().hint_min_opt_level,
870872
state.ws.is_member(pkg),
871873
is_local,
872874
unit_for,

src/diagnostics/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//! - TOML syntax or manifest schema: [`passes::emit_parse_diagnostics`], [`rules::PARSE_PASS_RULES`]
2121
//! - Lockfile
2222
//! - May be overly broad for what dependencies are checked
23-
//! - Pre-build unit graph
23+
//! - Pre-build unit graph: [`rules::min_opt_level_hint::diagnose`]
2424
//! - Tailored to a specific configuration (features, targets) but requires users to enumerate every configuration
2525
//! - Post-build unit graph: [`rules::unused_dependencies::lint_build_results`]
2626
//! - Slow feedback cycle since a build needs to happen
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use crate::CargoResult;
2+
use crate::compiler::BuildContext;
3+
use crate::workspace::profiles::{MinOptLevelHintError, parse_min_opt_level_hint};
4+
5+
/// Emits diagnostics for `hints.min-opt-level` once per package selected for compilation.
6+
#[tracing::instrument(skip_all)]
7+
pub(crate) fn diagnose(bcx: &BuildContext<'_, '_>) -> CargoResult<()> {
8+
let gctx = bcx.gctx;
9+
let mut packages = bcx
10+
.unit_graph
11+
.keys()
12+
.filter(|unit| !unit.skip_non_compile_time_dep && unit.show_warnings(gctx))
13+
.map(|unit| unit.pkg.clone())
14+
.collect::<Vec<_>>();
15+
packages.sort_by_key(|pkg| pkg.package_id());
16+
packages.dedup_by_key(|pkg| pkg.package_id());
17+
18+
for pkg in packages {
19+
let warn = |message: &str| {
20+
gctx.shell()
21+
.warn(format!("{}@{}: {message}", pkg.name(), pkg.version()))
22+
};
23+
let min_opt_level = match parse_min_opt_level_hint(
24+
pkg.hints().and_then(|hints| hints.min_opt_level.as_ref()),
25+
) {
26+
Ok(level) => level,
27+
Err(MinOptLevelHintError::OutOfRange(level)) => {
28+
warn(&format!(
29+
"ignoring unsupported value ({level}) for 'hints.min-opt-level', which only supports integers from 0 to 3"
30+
))?;
31+
None
32+
}
33+
Err(MinOptLevelHintError::WrongType(value_type)) => {
34+
warn(&format!(
35+
"ignoring unsupported value type ({value_type}) for 'hints.min-opt-level', which expects an integer"
36+
))?;
37+
None
38+
}
39+
};
40+
41+
if matches!(min_opt_level, Some(1..=3)) && !gctx.cli_unstable().hint_min_opt_level {
42+
warn("ignoring 'hints.min-opt-level', pass `-Zhint-min-opt-level` to enable it")?;
43+
}
44+
}
45+
46+
Ok(())
47+
}

src/diagnostics/rules/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod blanket_hint_mostly_unused;
22
mod deferred_parse_diagnostics;
33
mod im_a_teapot;
44
mod manual_readme;
5+
pub mod min_opt_level_hint;
56
mod missing_lints_features;
67
mod missing_lints_inheritance;
78
mod non_kebab_case_bins;

0 commit comments

Comments
 (0)