Skip to content

Commit 82506df

Browse files
Add unstable_rust_features attribute (#3963)
This attribute can be set to the label of a target that provides `UnstableRustFeaturesInfo`, which maps a label to a set of unstable features allowed for that label. The intent is to provide a way to centrally manage the use of unstable features, without having to use `-Zallow-features=` manually in `rustc_flags` for every affected target. Co-authored-by: Krasimir Georgiev <krasimir@google.com>
1 parent 325e942 commit 82506df

11 files changed

Lines changed: 148 additions & 6 deletions

File tree

.bazelci/presubmit.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,12 @@ tasks:
630630
- "--config=no_std_alloc"
631631
test_flags:
632632
- "--config=no_std_alloc"
633+
unstable_rust_features_ubuntu2204:
634+
name: Build with unstable_rust_features
635+
platform: ubuntu2204
636+
working_directory: test/integration/unstable_rust_features
637+
build_targets:
638+
- "//..."
633639
bzlmod_repo_mapping_runfiles:
634640
name: bzlmod repo mapping test
635641
platform: ubuntu2204

rust/private/providers.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,8 @@ AllocatorLibrariesImplInfo = provider(
229229
"static_archive": "Optional[File]: the allocator library archive (typically .a file).",
230230
},
231231
)
232+
233+
UnstableRustFeaturesInfo = provider(
234+
doc = "UnstableRustFeaturesInfo contains a function mapping build targets to unstable features approved for use. Only works on nightly toolchains. May return the special value \"__all__\" to allow all unstable features for the target.",
235+
fields = {"unstable_rust_features_config": "Callable[[Label], List[string]] Returns a list of unstable features approved for use for the given build target."},
236+
)

rust/private/rust.bzl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ load(
2424
"CrateGroupInfo",
2525
"CrateInfo",
2626
"LintsInfo",
27+
"UnstableRustFeaturesInfo",
2728
)
2829
load(
2930
":rust_allocator_libraries.bzl",
@@ -807,6 +808,11 @@ _COMMON_ATTRS = {
807808
"stamp": _stamp_attribute(
808809
default_value = 0,
809810
),
811+
"unstable_rust_features_config": attr.label(
812+
doc = "Controls which unstable features are allowed to be used by this target. Setting this to anything other than None requires a nightly toolchain.",
813+
providers = [UnstableRustFeaturesInfo],
814+
default = None,
815+
),
810816
"version": attr.string(
811817
doc = "A version to inject in the cargo environment variable.",
812818
default = "0.0.0",

rust/private/rustc.bzl

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ load(
3434
"AlwaysEnableMetadataOutputGroupsInfo",
3535
"LintsInfo",
3636
"RustcOutputDiagnosticsInfo",
37+
"UnstableRustFeaturesInfo",
3738
_BuildInfo = "BuildInfo",
3839
)
3940
load(":rustc_resource_set.bzl", "get_rustc_resource_set", "is_codegen_units_enabled")
@@ -904,6 +905,15 @@ def _should_add_oso_prefix(toolchain):
904905

905906
return True
906907

908+
def _extract_allowed_unstable_features_from_flags(rust_flags, all_allowed_unstable_features):
909+
other_flags = []
910+
for flag in rust_flags:
911+
if flag.startswith("-Zallow-features="):
912+
all_allowed_unstable_features.extend(flag.removeprefix("-Zallow-features=").split(","))
913+
else:
914+
other_flags.append(flag)
915+
return other_flags
916+
907917
def construct_arguments(
908918
*,
909919
ctx,
@@ -933,7 +943,8 @@ def construct_arguments(
933943
force_depend_on_objects = False,
934944
skip_expanding_rustc_env = False,
935945
require_explicit_unstable_features = False,
936-
error_format = None):
946+
error_format = None,
947+
allowed_unstable_rust_features = None):
937948
"""Builds an Args object containing common rustc flags
938949
939950
Args:
@@ -969,6 +980,7 @@ def construct_arguments(
969980
skip_expanding_rustc_env (bool): Whether to skip expanding CrateInfo.rustc_env_attr
970981
require_explicit_unstable_features (bool): Whether to require all unstable features to be explicitly opted in to using `-Zallow-features=...`.
971982
error_format (str, optional): Error format to pass to the `--error-format` command line argument. If set to None, uses the "_error_format" entry in `attr`.
983+
allowed_unstable_rust_features (list, optional): List of unstable Rust language features allowed for this target.
972984
973985
Returns:
974986
tuple: A tuple of the following items
@@ -996,8 +1008,12 @@ def construct_arguments(
9961008

9971009
process_wrapper_flags.add_all(build_flags_files, before_each = "--arg-file")
9981010

999-
if require_explicit_unstable_features:
1000-
process_wrapper_flags.add("--require-explicit-unstable-features", "true")
1011+
all_allowed_unstable_features = []
1012+
if getattr(ctx.attr, "unstable_rust_features_config", None):
1013+
all_allowed_unstable_features.extend(ctx.attr.unstable_rust_features_config[UnstableRustFeaturesInfo].unstable_rust_features_config(ctx.label))
1014+
1015+
if allowed_unstable_rust_features != None:
1016+
all_allowed_unstable_features.extend(allowed_unstable_rust_features)
10011017

10021018
# Certain rust build processes expect to find files from the environment
10031019
# variable `$CARGO_MANIFEST_DIR`. Examples of this include pest, tera,
@@ -1124,7 +1140,11 @@ def construct_arguments(
11241140

11251141
# Tell Rustc where to find the standard library (or libcore)
11261142
rustc_flags.add_all(toolchain.rust_std_paths, before_each = "-L", format_each = "%s")
1127-
rustc_flags.add_all(rust_flags, map_each = map_flag)
1143+
1144+
rustc_flags.add_all(
1145+
_extract_allowed_unstable_features_from_flags(rust_flags, all_allowed_unstable_features),
1146+
map_each = map_flag,
1147+
)
11281148

11291149
# Gather data path from crate_info since it is inherited from real crate for rust_doc and rust_test
11301150
# Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681
@@ -1244,7 +1264,18 @@ def construct_arguments(
12441264
if hasattr(ctx.attr, "_extra_exec_rustc_env") and is_exec_configuration(ctx):
12451265
env.update(ctx.attr._extra_exec_rustc_env[ExtraExecRustcEnvInfo].extra_exec_rustc_env)
12461266

1247-
rustc_flags.add_all(collect_extra_rustc_flags(ctx, toolchain, crate_info.root, crate_info.type), map_each = map_flag)
1267+
extra_rustc_flags = _extract_allowed_unstable_features_from_flags(
1268+
collect_extra_rustc_flags(ctx, toolchain, crate_info.root, crate_info.type),
1269+
all_allowed_unstable_features,
1270+
)
1271+
if getattr(ctx.attr, "unstable_rust_features_config", None) and not "__all__" in all_allowed_unstable_features:
1272+
all_allowed_unstable_features = {f: None for f in all_allowed_unstable_features}.keys()
1273+
extra_rustc_flags.append("-Zallow-features=" + ",".join(all_allowed_unstable_features))
1274+
1275+
# require_explicit_unstable_features makes no sense when all features are allowed anyway
1276+
if require_explicit_unstable_features:
1277+
process_wrapper_flags.add("--require-explicit-unstable-features", "true")
1278+
rustc_flags.add_all(extra_rustc_flags, map_each = map_flag)
12481279

12491280
if is_no_std(ctx, toolchain, crate_info.is_test):
12501281
rustc_flags.add('--cfg=feature="no_std"')
@@ -1323,7 +1354,8 @@ def rustc_compile_action(
13231354
force_all_deps_direct = False,
13241355
crate_info_dict = None,
13251356
skip_expanding_rustc_env = False,
1326-
include_coverage = True):
1357+
include_coverage = True,
1358+
allowed_unstable_rust_features = None):
13271359
"""Create and run a rustc compile action based on the current rule's attributes
13281360
13291361
Args:
@@ -1337,6 +1369,8 @@ def rustc_compile_action(
13371369
crate_info_dict: A mutable dict used to create CrateInfo provider
13381370
skip_expanding_rustc_env (bool, optional): Whether to expand CrateInfo.rustc_env
13391371
include_coverage (bool, optional): Whether to generate coverage information or not.
1372+
allowed_unstable_rust_features (list, optional): A list of unstable Rust language features
1373+
that are allowed to be used in the crate.
13401374
13411375
Returns:
13421376
list: A list of the following providers:
@@ -1460,6 +1494,7 @@ def rustc_compile_action(
14601494
use_json_output = bool(build_metadata) or bool(rustc_output) or bool(rustc_rmeta_output),
14611495
skip_expanding_rustc_env = skip_expanding_rustc_env,
14621496
require_explicit_unstable_features = require_explicit_unstable_features,
1497+
allowed_unstable_rust_features = allowed_unstable_rust_features,
14631498
)
14641499

14651500
args_metadata = None
@@ -1487,6 +1522,7 @@ def rustc_compile_action(
14871522
use_json_output = True,
14881523
build_metadata = True,
14891524
require_explicit_unstable_features = require_explicit_unstable_features,
1525+
allowed_unstable_rust_features = allowed_unstable_rust_features,
14901526
)
14911527

14921528
env = dict(ctx.configuration.default_shell_env)

rust/rust_common.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ load(
2727
_DepInfo = "DepInfo",
2828
_DepVariantInfo = "DepVariantInfo",
2929
_TestCrateInfo = "TestCrateInfo",
30+
_UnstableRustFeaturesInfo = "UnstableRustFeaturesInfo",
3031
)
3132

3233
BuildInfo = _BuildInfo
@@ -36,5 +37,6 @@ CrateInfo = _CrateInfo
3637
DepInfo = _DepInfo
3738
DepVariantInfo = _DepVariantInfo
3839
TestCrateInfo = _TestCrateInfo
40+
UnstableRustFeaturesInfo = _UnstableRustFeaturesInfo
3941

4042
COMMON_PROVIDERS = _COMMON_PROVIDERS
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
###############################################################################
2+
## Incompatibility flags
3+
###############################################################################
4+
5+
# https://github.com/bazelbuild/bazel/issues/8195
6+
build --incompatible_disallow_empty_glob=true
7+
8+
# https://github.com/bazelbuild/bazel/issues/12821
9+
build --nolegacy_external_runfiles
10+
11+
# https://github.com/bazelbuild/bazel/issues/23043.
12+
build --incompatible_autoload_externally=
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
load("@rules_rust//rust:defs.bzl", "rust_binary")
2+
load(":unstable_features_for_test.bzl", "unstable_rust_features_for_test_rule")
3+
4+
unstable_rust_features_for_test_rule(name = "unstable_rust_features_for_test")
5+
6+
rust_binary(
7+
name = "unstable_features_test",
8+
srcs = ["main.rs"],
9+
unstable_rust_features_config = ":unstable_rust_features_for_test",
10+
)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module(
2+
name = "rules_rust_test_unstable_rust_features",
3+
version = "0.0.0",
4+
)
5+
6+
bazel_dep(name = "rules_rust", version = "0.0.0")
7+
local_path_override(
8+
module_name = "rules_rust",
9+
path = "../../..",
10+
)
11+
12+
bazel_dep(name = "rules_cc", version = "0.2.4")
13+
bazel_dep(name = "bazel_skylib", version = "1.8.2")
14+
bazel_dep(name = "platforms", version = "1.0.0")
15+
16+
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
17+
rust.toolchain(
18+
edition = "2018",
19+
)
20+
21+
# Use a nightly version where `core_intrinsics` is an unstable feature.
22+
VERSION = "nightly/2026-04-15"
23+
24+
rust.repository_set(
25+
name = "nightly_rust_x86_64",
26+
allocator_library = "@rules_rust//ffi/rs:empty",
27+
edition = "2021",
28+
exec_triple = "x86_64-unknown-linux-gnu",
29+
target_compatible_with = [
30+
"@platforms//cpu:x86_64",
31+
"@platforms//os:linux",
32+
],
33+
target_triple = "x86_64-unknown-linux-gnu",
34+
versions = [VERSION],
35+
)
36+
use_repo(rust, "rust_toolchains")
37+
38+
register_toolchains("@rust_toolchains//:all")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
workspace(name = "rules_rust_test_unstable_rust_features")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![feature(core_intrinsics)]
2+
#![allow(internal_features)]
3+
use std::intrinsics;
4+
5+
fn main() {
6+
intrinsics::abort();
7+
}

0 commit comments

Comments
 (0)