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:
bors 2024-10-25 07:42:32 +00:00
commit 97ae1df8aa
20 changed files with 273 additions and 259 deletions

View File

@ -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> {

View File

@ -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"

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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;
}

View File

@ -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>,

View File

@ -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>>,

View File

@ -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>>,

View File

@ -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. Its
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.

View File

@ -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"]

View File

@ -1,5 +1,5 @@
//@ compile-flags: -Znext-solver
//@ known-bug: unknown
//@ check-pass
#![allow(incomplete_features)]
#![feature(const_trait_impl, effects)]

View File

@ -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`.

View File

@ -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`.

View File

@ -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() {}

View File

@ -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`.

View File

@ -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() {}

View File

@ -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`.