mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Auto merge of #132128 - workingjubilee:rollup-uwqp2i2, r=workingjubilee
Rollup of 4 pull requests Successful merges: - #131457 (Expand `ptr::fn_addr_eq()` documentation.) - #132085 (Update StableMIR doc to reflect current status) - #132118 (Add support for `~const` item bounds) - #132125 (coverage: Emit LLVM intrinsics using the normal helper method) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
97ae1df8aa
@ -1725,16 +1725,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
fn fptosi_sat(&mut self, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||
self.fptoint_sat(true, val, dest_ty)
|
||||
}
|
||||
|
||||
fn instrprof_increment(
|
||||
&mut self,
|
||||
_fn_name: RValue<'gcc>,
|
||||
_hash: RValue<'gcc>,
|
||||
_num_counters: RValue<'gcc>,
|
||||
_index: RValue<'gcc>,
|
||||
) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||
|
@ -1165,39 +1165,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size);
|
||||
}
|
||||
|
||||
fn instrprof_increment(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
num_counters: &'ll Value,
|
||||
index: &'ll Value,
|
||||
) {
|
||||
debug!(
|
||||
"instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})",
|
||||
fn_name, hash, num_counters, index
|
||||
);
|
||||
|
||||
let llfn = unsafe { llvm::LLVMRustGetInstrProfIncrementIntrinsic(self.cx().llmod) };
|
||||
let llty = self.cx.type_func(
|
||||
&[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32(), self.cx.type_i32()],
|
||||
self.cx.type_void(),
|
||||
);
|
||||
let args = &[fn_name, hash, num_counters, index];
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
llty: &'ll Type,
|
||||
@ -1667,6 +1634,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
kcfi_bundle
|
||||
}
|
||||
|
||||
/// Emits a call to `llvm.instrprof.increment`. Used by coverage instrumentation.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn instrprof_increment(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
num_counters: &'ll Value,
|
||||
index: &'ll Value,
|
||||
) {
|
||||
self.call_intrinsic("llvm.instrprof.increment", &[fn_name, hash, num_counters, index]);
|
||||
}
|
||||
|
||||
/// Emits a call to `llvm.instrprof.mcdc.parameters`.
|
||||
///
|
||||
/// This doesn't produce any code directly, but is used as input by
|
||||
@ -1676,40 +1655,21 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
///
|
||||
/// [`CodeGenPGO::emitMCDCParameters`]:
|
||||
/// https://github.com/rust-lang/llvm-project/blob/5399a24/clang/lib/CodeGen/CodeGenPGO.cpp#L1124
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn mcdc_parameters(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
bitmap_bits: &'ll Value,
|
||||
) {
|
||||
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bits);
|
||||
|
||||
assert!(
|
||||
crate::llvm_util::get_version() >= (19, 0, 0),
|
||||
"MCDC intrinsics require LLVM 19 or later"
|
||||
);
|
||||
|
||||
let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) };
|
||||
let llty = self.cx.type_func(
|
||||
&[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32()],
|
||||
self.cx.type_void(),
|
||||
);
|
||||
let args = &[fn_name, hash, bitmap_bits];
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
}
|
||||
self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[fn_name, hash, bitmap_bits]);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn mcdc_tvbitmap_update(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
@ -1717,39 +1677,21 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
bitmap_index: &'ll Value,
|
||||
mcdc_temp: &'ll Value,
|
||||
) {
|
||||
debug!(
|
||||
"mcdc_tvbitmap_update() with args ({:?}, {:?}, {:?}, {:?})",
|
||||
fn_name, hash, bitmap_index, mcdc_temp
|
||||
);
|
||||
assert!(
|
||||
crate::llvm_util::get_version() >= (19, 0, 0),
|
||||
"MCDC intrinsics require LLVM 19 or later"
|
||||
);
|
||||
|
||||
let llfn =
|
||||
unsafe { llvm::LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(self.cx().llmod) };
|
||||
let llty = self.cx.type_func(
|
||||
&[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32(), self.cx.type_ptr()],
|
||||
self.cx.type_void(),
|
||||
);
|
||||
let args = &[fn_name, hash, bitmap_index, mcdc_temp];
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
}
|
||||
self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", args);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn mcdc_condbitmap_reset(&mut self, mcdc_temp: &'ll Value) {
|
||||
self.store(self.const_i32(0), mcdc_temp, self.tcx.data_layout.i32_align.abi);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn mcdc_condbitmap_update(&mut self, cond_index: &'ll Value, mcdc_temp: &'ll Value) {
|
||||
debug!("mcdc_condbitmap_update() with args ({:?}, {:?})", cond_index, mcdc_temp);
|
||||
assert!(
|
||||
crate::llvm_util::get_version() >= (19, 0, 0),
|
||||
"MCDC intrinsics require LLVM 19 or later"
|
||||
|
@ -1099,6 +1099,10 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
|
||||
if self.sess().instrument_coverage() {
|
||||
ifn!("llvm.instrprof.increment", fn(ptr, t_i64, t_i32, t_i32) -> void);
|
||||
if crate::llvm_util::get_version() >= (19, 0, 0) {
|
||||
ifn!("llvm.instrprof.mcdc.parameters", fn(ptr, t_i64, t_i32) -> void);
|
||||
ifn!("llvm.instrprof.mcdc.tvbitmap.update", fn(ptr, t_i64, t_i32, ptr) -> void);
|
||||
}
|
||||
}
|
||||
|
||||
ifn!("llvm.type.test", fn(ptr, t_metadata) -> i1);
|
||||
|
@ -208,6 +208,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
let bitmap_index = bx.const_u32(bitmap_idx);
|
||||
bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap);
|
||||
bx.mcdc_condbitmap_reset(cond_bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1615,10 +1615,6 @@ unsafe extern "C" {
|
||||
pub fn LLVMRustSetAllowReassoc(Instr: &Value);
|
||||
|
||||
// Miscellaneous instructions
|
||||
pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value;
|
||||
pub fn LLVMRustGetInstrProfMCDCParametersIntrinsic(M: &Module) -> &Value;
|
||||
pub fn LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(M: &Module) -> &Value;
|
||||
|
||||
pub fn LLVMRustBuildCall<'a>(
|
||||
B: &Builder<'a>,
|
||||
Ty: &'a Type,
|
||||
|
@ -437,14 +437,6 @@ pub trait BuilderMethods<'a, 'tcx>:
|
||||
/// Called for `StorageDead`
|
||||
fn lifetime_end(&mut self, ptr: Self::Value, size: Size);
|
||||
|
||||
fn instrprof_increment(
|
||||
&mut self,
|
||||
fn_name: Self::Value,
|
||||
hash: Self::Value,
|
||||
num_counters: Self::Value,
|
||||
index: Self::Value,
|
||||
);
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
llty: Self::Type,
|
||||
|
@ -1531,45 +1531,6 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty,
|
||||
ArrayRef<OperandBundleDef>(OpBundles)));
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef
|
||||
LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) {
|
||||
#if LLVM_VERSION_GE(20, 0)
|
||||
return wrap(llvm::Intrinsic::getOrInsertDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_increment));
|
||||
#else
|
||||
return wrap(llvm::Intrinsic::getDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_increment));
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef
|
||||
LLVMRustGetInstrProfMCDCParametersIntrinsic(LLVMModuleRef M) {
|
||||
#if LLVM_VERSION_LT(19, 0)
|
||||
report_fatal_error("LLVM 19.0 is required for mcdc intrinsic functions");
|
||||
#endif
|
||||
#if LLVM_VERSION_GE(20, 0)
|
||||
return wrap(llvm::Intrinsic::getOrInsertDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters));
|
||||
#else
|
||||
return wrap(llvm::Intrinsic::getDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters));
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef
|
||||
LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) {
|
||||
#if LLVM_VERSION_LT(19, 0)
|
||||
report_fatal_error("LLVM 19.0 is required for mcdc intrinsic functions");
|
||||
#endif
|
||||
#if LLVM_VERSION_GE(20, 0)
|
||||
return wrap(llvm::Intrinsic::getOrInsertDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update));
|
||||
#else
|
||||
return wrap(llvm::Intrinsic::getDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update));
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst,
|
||||
unsigned DstAlign, LLVMValueRef Src,
|
||||
unsigned SrcAlign,
|
||||
|
@ -100,6 +100,15 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Assemble additional assumptions for an alias that are not included
|
||||
/// in the item bounds of the alias. For now, this is limited to the
|
||||
/// `implied_const_bounds` for an associated type.
|
||||
fn consider_additional_alias_assumptions(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
alias_ty: ty::AliasTy<I>,
|
||||
) -> Vec<Candidate<I>>;
|
||||
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
@ -594,6 +603,8 @@ where
|
||||
));
|
||||
}
|
||||
|
||||
candidates.extend(G::consider_additional_alias_assumptions(self, goal, alias_ty));
|
||||
|
||||
if kind != ty::Projection {
|
||||
return;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
use rustc_type_ir::fast_reject::DeepRejectCtxt;
|
||||
use rustc_type_ir::inherent::*;
|
||||
use rustc_type_ir::{self as ty, Interner};
|
||||
use rustc_type_ir::{self as ty, Interner, elaborate};
|
||||
use tracing::instrument;
|
||||
|
||||
use super::assembly::Candidate;
|
||||
@ -70,6 +70,55 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Register additional assumptions for aliases corresponding to `~const` item bounds.
|
||||
///
|
||||
/// Unlike item bounds, they are not simply implied by the well-formedness of the alias.
|
||||
/// Instead, they only hold if the const conditons on the alias also hold. This is why
|
||||
/// we also register the const conditions of the alias after matching the goal against
|
||||
/// the assumption.
|
||||
fn consider_additional_alias_assumptions(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
alias_ty: ty::AliasTy<I>,
|
||||
) -> Vec<Candidate<I>> {
|
||||
let cx = ecx.cx();
|
||||
let mut candidates = vec![];
|
||||
|
||||
// FIXME(effects): We elaborate here because the implied const bounds
|
||||
// aren't necessarily elaborated. We probably should prefix this query
|
||||
// with `explicit_`...
|
||||
for clause in elaborate::elaborate(
|
||||
cx,
|
||||
cx.implied_const_bounds(alias_ty.def_id)
|
||||
.iter_instantiated(cx, alias_ty.args)
|
||||
.map(|trait_ref| trait_ref.to_host_effect_clause(cx, goal.predicate.host)),
|
||||
) {
|
||||
candidates.extend(Self::probe_and_match_goal_against_assumption(
|
||||
ecx,
|
||||
CandidateSource::AliasBound,
|
||||
goal,
|
||||
clause,
|
||||
|ecx| {
|
||||
// Const conditions must hold for the implied const bound to hold.
|
||||
ecx.add_goals(
|
||||
GoalSource::Misc,
|
||||
cx.const_conditions(alias_ty.def_id)
|
||||
.iter_instantiated(cx, alias_ty.args)
|
||||
.map(|trait_ref| {
|
||||
goal.with(
|
||||
cx,
|
||||
trait_ref.to_host_effect_clause(cx, goal.predicate.host),
|
||||
)
|
||||
}),
|
||||
);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
candidates
|
||||
}
|
||||
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
|
@ -193,6 +193,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_additional_alias_assumptions(
|
||||
_ecx: &mut EvalCtxt<'_, D>,
|
||||
_goal: Goal<I, Self>,
|
||||
_alias_ty: ty::AliasTy<I>,
|
||||
) -> Vec<Candidate<I>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, NormalizesTo<I>>,
|
||||
|
@ -39,6 +39,14 @@ where
|
||||
self.def_id()
|
||||
}
|
||||
|
||||
fn consider_additional_alias_assumptions(
|
||||
_ecx: &mut EvalCtxt<'_, D>,
|
||||
_goal: Goal<I, Self>,
|
||||
_alias_ty: ty::AliasTy<I>,
|
||||
) -> Vec<Candidate<I>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, TraitPredicate<I>>,
|
||||
|
@ -1,93 +1,33 @@
|
||||
This crate is regularly synced with its mirror in the rustc repo at `compiler/rustc_smir`.
|
||||
This crate is currently developed in-tree together with the compiler.
|
||||
|
||||
We use `git subtree` for this to preserve commits and allow the rustc repo to
|
||||
edit these crates without having to touch this repo. This keeps the crates compiling
|
||||
while allowing us to independently work on them here. The effort of keeping them in
|
||||
sync is pushed entirely onto us, without affecting rustc workflows negatively.
|
||||
This may change in the future, but changes to policy should only be done via a
|
||||
compiler team MCP.
|
||||
Our goal is to start publishing `stable_mir` into crates.io.
|
||||
Until then, users will use this as any other rustc crate, by installing
|
||||
the rustup component `rustc-dev`, and declaring `stable-mir` as an external crate.
|
||||
|
||||
## Instructions for working on this crate locally
|
||||
|
||||
Since the crate is the same in the rustc repo and here, the dependencies on rustc_* crates
|
||||
will only either work here or there, but never in both places at the same time. Thus we use
|
||||
optional dependencies on the rustc_* crates, requiring local development to use
|
||||
|
||||
```
|
||||
cargo build --no-default-features -Zavoid-dev-deps
|
||||
```
|
||||
|
||||
in order to compile successfully.
|
||||
|
||||
## Instructions for syncing
|
||||
|
||||
### Updating this repository
|
||||
|
||||
In the rustc repo, execute
|
||||
|
||||
```
|
||||
git subtree push --prefix=compiler/rustc_smir url_to_your_fork_of_project_stable_mir some_feature_branch
|
||||
```
|
||||
|
||||
and then open a PR of your `some_feature_branch` against https://github.com/rust-lang/project-stable-mir
|
||||
|
||||
### Updating the rustc library
|
||||
|
||||
First we need to bump our stack limit, as the rustc repo otherwise quickly hits that:
|
||||
|
||||
```
|
||||
ulimit -s 60000
|
||||
```
|
||||
|
||||
#### Maximum function recursion depth (1000) reached
|
||||
|
||||
Then we need to disable `dash` as the default shell for sh scripts, as otherwise we run into a
|
||||
hard limit of a recursion depth of 1000:
|
||||
|
||||
```
|
||||
sudo dpkg-reconfigure dash
|
||||
```
|
||||
|
||||
and then select `No` to disable dash.
|
||||
|
||||
|
||||
#### Patching your `git worktree`
|
||||
|
||||
The regular git worktree does not scale to repos of the size of the rustc repo.
|
||||
So download the `git-subtree.sh` from https://github.com/gitgitgadget/git/pull/493/files and run
|
||||
|
||||
```
|
||||
sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree
|
||||
sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
|
||||
sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
|
||||
```
|
||||
|
||||
#### Actually doing a sync
|
||||
|
||||
In the rustc repo, execute
|
||||
|
||||
```
|
||||
git subtree pull --prefix=compiler/rustc_smir https://github.com/rust-lang/project-stable-mir smir
|
||||
```
|
||||
|
||||
Note: only ever sync to rustc from the project-stable-mir's `smir` branch. Do not sync with your own forks.
|
||||
|
||||
Then open a PR against rustc just like a regular PR.
|
||||
See the StableMIR ["Getting Started"](https://rust-lang.github.io/project-stable-mir/getting-started.html)
|
||||
guide for more information.
|
||||
|
||||
## Stable MIR Design
|
||||
|
||||
The stable-mir will follow a similar approach to proc-macro2. It’s
|
||||
implementation will eventually be broken down into two main crates:
|
||||
The stable-mir will follow a similar approach to proc-macro2. Its
|
||||
implementation is split between two main crates:
|
||||
|
||||
- `stable_mir`: Public crate, to be published on crates.io, which will contain
|
||||
the stable data structure as well as proxy APIs to make calls to the
|
||||
compiler.
|
||||
- `rustc_smir`: The compiler crate that will translate from internal MIR to
|
||||
SMIR. This crate will also implement APIs that will be invoked by
|
||||
stable-mir to query the compiler for more information.
|
||||
the stable data structure as well as calls to `rustc_smir` APIs. The
|
||||
translation between stable and internal constructs will also be done in this crate,
|
||||
however, this is currently implemented in the `rustc_smir` crate.[^translation].
|
||||
- `rustc_smir`: This crate implements the public APIs to the compiler.
|
||||
It is responsible for gathering all the information requested, and providing
|
||||
the data in its unstable form.
|
||||
|
||||
This will help tools to communicate with the rust compiler via stable APIs. Tools will depend on
|
||||
`stable_mir` crate, which will invoke the compiler using APIs defined in `rustc_smir`. I.e.:
|
||||
[^translation]: This is currently implemented in the `rustc_smir` crate,
|
||||
but we are working to change that.
|
||||
|
||||
I.e.,
|
||||
tools will depend on `stable_mir` crate,
|
||||
which will invoke the compiler using APIs defined in `rustc_smir`.
|
||||
|
||||
I.e.:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────┐ ┌──────────────────────────────────┐
|
||||
@ -104,9 +44,3 @@ This will help tools to communicate with the rust compiler via stable APIs. Tool
|
||||
|
||||
More details can be found here:
|
||||
https://hackmd.io/XhnYHKKuR6-LChhobvlT-g?view
|
||||
|
||||
For now, the code for these two crates are in separate modules of this crate.
|
||||
The modules have the same name for simplicity. We also have a third module,
|
||||
`rustc_internal` which will expose APIs and definitions that allow users to
|
||||
gather information from internal MIR constructs that haven't been exposed in
|
||||
the `stable_mir` module.
|
||||
|
@ -2107,13 +2107,39 @@ pub fn addr_eq<T: ?Sized, U: ?Sized>(p: *const T, q: *const U) -> bool {
|
||||
|
||||
/// Compares the *addresses* of the two function pointers for equality.
|
||||
///
|
||||
/// Function pointers comparisons can have surprising results since
|
||||
/// they are never guaranteed to be unique and could vary between different
|
||||
/// code generation units. Furthermore, different functions could have the
|
||||
/// same address after being merged together.
|
||||
/// This is the same as `f == g`, but using this function makes clear that the potentially
|
||||
/// surprising semantics of function pointer comparison are involved.
|
||||
///
|
||||
/// There are **very few guarantees** about how functions are compiled and they have no intrinsic
|
||||
/// “identity”; in particular, this comparison:
|
||||
///
|
||||
/// * May return `true` unexpectedly, in cases where functions are equivalent.
|
||||
///
|
||||
/// For example, the following program is likely (but not guaranteed) to print `(true, true)`
|
||||
/// when compiled with optimization:
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(ptr_fn_addr_eq)]
|
||||
/// let f: fn(i32) -> i32 = |x| x;
|
||||
/// let g: fn(i32) -> i32 = |x| x + 0; // different closure, different body
|
||||
/// let h: fn(u32) -> u32 = |x| x + 0; // different signature too
|
||||
/// dbg!(std::ptr::fn_addr_eq(f, g), std::ptr::fn_addr_eq(f, h)); // not guaranteed to be equal
|
||||
/// ```
|
||||
///
|
||||
/// * May return `false` in any case.
|
||||
///
|
||||
/// This is particularly likely with generic functions but may happen with any function.
|
||||
/// (From an implementation perspective, this is possible because functions may sometimes be
|
||||
/// processed more than once by the compiler, resulting in duplicate machine code.)
|
||||
///
|
||||
/// Despite these false positives and false negatives, this comparison can still be useful.
|
||||
/// Specifically, if
|
||||
///
|
||||
/// * `T` is the same type as `U`, `T` is a [subtype] of `U`, or `U` is a [subtype] of `T`, and
|
||||
/// * `ptr::fn_addr_eq(f, g)` returns true,
|
||||
///
|
||||
/// then calling `f` and calling `g` will be equivalent.
|
||||
///
|
||||
/// This is the same as `f == g` but using this function makes clear
|
||||
/// that you are aware of these potentially surprising semantics.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -2125,6 +2151,8 @@ pub fn addr_eq<T: ?Sized, U: ?Sized>(p: *const T, q: *const U) -> bool {
|
||||
/// fn b() { println!("b"); }
|
||||
/// assert!(!ptr::fn_addr_eq(a as fn(), b as fn()));
|
||||
/// ```
|
||||
///
|
||||
/// [subtype]: https://doc.rust-lang.org/reference/subtyping.html
|
||||
#[unstable(feature = "ptr_fn_addr_eq", issue = "129322")]
|
||||
#[inline(always)]
|
||||
#[must_use = "function pointer comparison produces a value"]
|
||||
|
@ -1,5 +1,5 @@
|
||||
//@ compile-flags: -Znext-solver
|
||||
//@ known-bug: unknown
|
||||
//@ check-pass
|
||||
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(const_trait_impl, effects)]
|
||||
|
@ -1,15 +0,0 @@
|
||||
error[E0271]: type mismatch resolving `<T as Trait>::Assoc == T`
|
||||
--> $DIR/assoc-type-const-bound-usage-0.rs:14:5
|
||||
|
|
||||
LL | T::Assoc::func()
|
||||
| ^^^^^^^^^^^^^^^^ types differ
|
||||
|
||||
error[E0271]: type mismatch resolving `<T as Trait>::Assoc == T`
|
||||
--> $DIR/assoc-type-const-bound-usage-0.rs:18:5
|
||||
|
|
||||
LL | <T as Trait>::Assoc::func()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0271`.
|
@ -6,18 +6,30 @@ LL | #![feature(const_trait_impl, effects, generic_const_exprs)]
|
||||
|
|
||||
= help: remove one of these features
|
||||
|
||||
error[E0271]: type mismatch resolving `<T as Trait>::Assoc == T`
|
||||
--> $DIR/assoc-type-const-bound-usage-1.rs:15:44
|
||||
error[E0284]: type annotations needed: cannot normalize `unqualified<T>::{constant#0}`
|
||||
--> $DIR/assoc-type-const-bound-usage-1.rs:15:37
|
||||
|
|
||||
LL | fn unqualified<T: const Trait>() -> Type<{ T::Assoc::func() }> {
|
||||
| ^^^^^^^^^^^^^^^^ types differ
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `unqualified<T>::{constant#0}`
|
||||
|
||||
error[E0271]: type mismatch resolving `<T as Trait>::Assoc == T`
|
||||
--> $DIR/assoc-type-const-bound-usage-1.rs:19:42
|
||||
error[E0284]: type annotations needed: cannot normalize `qualified<T>::{constant#0}`
|
||||
--> $DIR/assoc-type-const-bound-usage-1.rs:19:35
|
||||
|
|
||||
LL | fn qualified<T: const Trait>() -> Type<{ <T as Trait>::Assoc::func() }> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `qualified<T>::{constant#0}`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error[E0284]: type annotations needed: cannot normalize `unqualified<T>::{constant#0}`
|
||||
--> $DIR/assoc-type-const-bound-usage-1.rs:16:5
|
||||
|
|
||||
LL | Type
|
||||
| ^^^^ cannot normalize `unqualified<T>::{constant#0}`
|
||||
|
||||
For more information about this error, try `rustc --explain E0271`.
|
||||
error[E0284]: type annotations needed: cannot normalize `qualified<T>::{constant#0}`
|
||||
--> $DIR/assoc-type-const-bound-usage-1.rs:20:5
|
||||
|
|
||||
LL | Type
|
||||
| ^^^^ cannot normalize `qualified<T>::{constant#0}`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0284`.
|
||||
|
@ -0,0 +1,35 @@
|
||||
//@ compile-flags: -Znext-solver
|
||||
|
||||
// Check that `~const` item bounds only hold if the where clauses on the
|
||||
// associated type are also const.
|
||||
// i.e. check that we validate the const conditions for the associated type
|
||||
// when considering one of implied const bounds.
|
||||
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(const_trait_impl, effects)]
|
||||
|
||||
#[const_trait]
|
||||
trait Trait {
|
||||
type Assoc<U>: ~const Trait
|
||||
where
|
||||
U: ~const Other;
|
||||
|
||||
fn func();
|
||||
}
|
||||
|
||||
#[const_trait]
|
||||
trait Other {}
|
||||
|
||||
const fn fails<T: ~const Trait, U: Other>() {
|
||||
T::Assoc::<U>::func();
|
||||
//~^ ERROR the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
|
||||
<T as Trait>::Assoc::<U>::func();
|
||||
//~^ ERROR the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
|
||||
}
|
||||
|
||||
const fn works<T: ~const Trait, U: ~const Other>() {
|
||||
T::Assoc::<U>::func();
|
||||
<T as Trait>::Assoc::<U>::func();
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,15 @@
|
||||
error[E0277]: the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
|
||||
--> $DIR/assoc-type-const-bound-usage-fail-2.rs:24:5
|
||||
|
|
||||
LL | T::Assoc::<U>::func();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0277]: the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
|
||||
--> $DIR/assoc-type-const-bound-usage-fail-2.rs:26:5
|
||||
|
|
||||
LL | <T as Trait>::Assoc::<U>::func();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -0,0 +1,28 @@
|
||||
//@ compile-flags: -Znext-solver
|
||||
|
||||
// Check that `~const` item bounds only hold if the parent trait is `~const`.
|
||||
// i.e. check that we validate the const conditions for the associated type
|
||||
// when considering one of implied const bounds.
|
||||
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(const_trait_impl, effects)]
|
||||
|
||||
#[const_trait]
|
||||
trait Trait {
|
||||
type Assoc: ~const Trait;
|
||||
fn func();
|
||||
}
|
||||
|
||||
const fn unqualified<T: Trait>() {
|
||||
T::Assoc::func();
|
||||
//~^ ERROR the trait bound `T: ~const Trait` is not satisfied
|
||||
<T as Trait>::Assoc::func();
|
||||
//~^ ERROR the trait bound `T: ~const Trait` is not satisfied
|
||||
}
|
||||
|
||||
const fn works<T: ~const Trait>() {
|
||||
T::Assoc::func();
|
||||
<T as Trait>::Assoc::func();
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,15 @@
|
||||
error[E0277]: the trait bound `T: ~const Trait` is not satisfied
|
||||
--> $DIR/assoc-type-const-bound-usage-fail.rs:17:5
|
||||
|
|
||||
LL | T::Assoc::func();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0277]: the trait bound `T: ~const Trait` is not satisfied
|
||||
--> $DIR/assoc-type-const-bound-usage-fail.rs:19:5
|
||||
|
|
||||
LL | <T as Trait>::Assoc::func();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user