1
Fork 0

Auto merge of #72330 - Dylan-DPC:rollup-yuxadv8, r=Dylan-DPC

Rollup of 5 pull requests

Successful merges:

 - #71599 (Support coercion between (FnDef | Closure) and (FnDef | Closure))
 - #71973 (Lazy normalization of constants (Reprise))
 - #72283 (Drop Elaboration Elaboration)
 - #72290 (Add newer rust versions to linker-plugin-lto.md)
 - #72318 (Add help text for remote-test-client)

Failed merges:

r? @ghost
This commit is contained in:
bors 2020-05-18 22:50:40 +00:00
commit 89988fe727
73 changed files with 1342 additions and 215 deletions

View file

@ -100,11 +100,17 @@ LLVM. However, the approximation is usually reliable.
The following table shows known good combinations of toolchain versions.
| | Clang 7 | Clang 8 |
|-----------|-----------|-----------|
| Rust 1.34 | ✗ | ✓ |
| Rust 1.35 | ✗ | ✓ |
| Rust 1.36 | ✗ | ✓ |
| Rust 1.37 | ✗ | ✓ |
| | Clang 7 | Clang 8 | Clang 9 |
|-----------|-----------|-----------|-----------|
| Rust 1.34 | ✗ | ✓ | ✗ |
| Rust 1.35 | ✗ | ✓ | ✗ |
| Rust 1.36 | ✗ | ✓ | ✗ |
| Rust 1.37 | ✗ | ✓ | ✗ |
| Rust 1.38 | ✗ | ✗ | ✓ |
| Rust 1.39 | ✗ | ✗ | ✓ |
| Rust 1.40 | ✗ | ✗ | ✓ |
| Rust 1.41 | ✗ | ✗ | ✓ |
| Rust 1.42 | ✗ | ✗ | ✓ |
| Rust 1.43 | ✗ | ✗ | ✓ |
Note that the compatibility policy for this feature might change in the future.

View file

@ -25,7 +25,7 @@ use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, BoundVar, Ty, TyCtxt};
use rustc_middle::ty::{self, BoundVar, Const, Ty, TyCtxt};
use std::fmt::Debug;
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
@ -671,6 +671,13 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
});
}
fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {
span_bug!(
self.cause.span(self.infcx.tcx),
"lazy_normalization_consts: unreachable `const_equate`"
);
}
fn normalization() -> NormalizationStrategy {
NormalizationStrategy::Eager
}

View file

@ -39,7 +39,7 @@ use rustc_hir::def_id::DefId;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{IntType, UintType};
use rustc_span::{Span, DUMMY_SP};
@ -126,7 +126,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
b: &'tcx ty::Const<'tcx>,
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>
where
R: TypeRelation<'tcx>,
R: ConstEquateRelation<'tcx>,
{
debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
if a == b {
@ -164,7 +164,22 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
return self.unify_const_variable(!a_is_expected, vid, a);
}
(ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => {
// FIXME(#59490): Need to remove the leak check to accomodate
// escaping bound variables here.
if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
relation.const_equate_obligation(a, b);
}
return Ok(b);
}
(_, ty::ConstKind::Unevaluated(..)) if self.tcx.lazy_normalization() => {
// FIXME(#59490): Need to remove the leak check to accomodate
// escaping bound variables here.
if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
relation.const_equate_obligation(a, b);
}
return Ok(a);
}
_ => {}
}
@ -375,6 +390,20 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
debug!("generalize: success {{ {:?}, {:?} }}", ty, needs_wf);
Ok(Generalization { ty, needs_wf })
}
pub fn add_const_equate_obligation(
&mut self,
a_is_expected: bool,
a: &'tcx ty::Const<'tcx>,
b: &'tcx ty::Const<'tcx>,
) {
let predicate = if a_is_expected {
ty::Predicate::ConstEquate(a, b)
} else {
ty::Predicate::ConstEquate(b, a)
};
self.obligations.push(Obligation::new(self.trace.cause.clone(), self.param_env, predicate));
}
}
struct Generalizer<'cx, 'tcx> {
@ -637,11 +666,19 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
}
}
}
ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(c),
_ => relate::super_relate_consts(self, c, c),
}
}
}
pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> {
/// Register an obligation that both constants must be equal to each other.
///
/// If they aren't equal then the relation doesn't hold.
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
}
pub trait RelateResultCompare<'tcx, T> {
fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T>
where

View file

@ -1,4 +1,4 @@
use super::combine::{CombineFields, RelationDir};
use super::combine::{CombineFields, ConstEquateRelation, RelationDir};
use super::Subtype;
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
@ -140,3 +140,9 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
}
}
}
impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> {
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
}
}

View file

@ -3,6 +3,7 @@ use super::lattice::{self, LatticeDir};
use super::InferCtxt;
use super::Subtype;
use crate::infer::combine::ConstEquateRelation;
use crate::traits::ObligationCause;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, Ty, TyCtxt};
@ -116,3 +117,9 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx,
Ok(())
}
}
impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> {
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
}
}

View file

@ -3,6 +3,7 @@ use super::lattice::{self, LatticeDir};
use super::InferCtxt;
use super::Subtype;
use crate::infer::combine::ConstEquateRelation;
use crate::traits::ObligationCause;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, Ty, TyCtxt};
@ -100,6 +101,12 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
}
}
impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> {
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
}
}
impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> {
fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> {
self.fields.infcx

View file

@ -1490,6 +1490,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
self.report_and_explain_type_error(trace, &err)
}
pub fn report_mismatched_consts(
&self,
cause: &ObligationCause<'tcx>,
expected: &'tcx ty::Const<'tcx>,
actual: &'tcx ty::Const<'tcx>,
err: TypeError<'tcx>,
) -> DiagnosticBuilder<'tcx> {
let trace = TypeTrace::consts(cause, true, expected, actual);
self.report_and_explain_type_error(trace, &err)
}
pub fn replace_bound_vars_with_fresh_vars<T>(
&self,
span: Span,
@ -1777,6 +1788,15 @@ impl<'tcx> TypeTrace<'tcx> {
TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) }
}
pub fn consts(
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: &'tcx ty::Const<'tcx>,
b: &'tcx ty::Const<'tcx>,
) -> TypeTrace<'tcx> {
TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) }
}
pub fn dummy(tcx: TyCtxt<'tcx>) -> TypeTrace<'tcx> {
TypeTrace {
cause: ObligationCause::dummy(),

View file

@ -21,6 +21,7 @@
//! thing we relate in chalk are basically domain goals and their
//! constituents)
use crate::infer::combine::ConstEquateRelation;
use crate::infer::InferCtxt;
use crate::infer::{ConstVarValue, ConstVariableValue};
use rustc_data_structures::fx::FxHashMap;
@ -77,6 +78,8 @@ pub trait TypeRelatingDelegate<'tcx> {
/// delegate.
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>);
fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
/// Creates a new universe index. Used when instantiating placeholders.
fn create_next_universe(&mut self) -> ty::UniverseIndex;
@ -715,6 +718,15 @@ where
}
}
impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
self.delegate.const_equate(a, b);
}
}
/// When we encounter a binder like `for<..> fn(..)`, we actually have
/// to walk the `fn` value to find all the values bound by the `for`
/// (these are not explicitly present in the ty representation right
@ -976,6 +988,7 @@ where
}
}
}
ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(a),
_ => relate::super_relate_consts(self, a, a),
}
}

View file

@ -19,7 +19,8 @@ pub fn explicit_outlives_bounds<'tcx>(
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::TypeOutlives(..)
| ty::Predicate::ConstEvaluatable(..) => None,
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => None,
ty::Predicate::RegionOutlives(ref data) => data
.no_bound_vars()
.map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)),

View file

@ -1,6 +1,7 @@
use super::combine::{CombineFields, RelationDir};
use super::SubregionOrigin;
use crate::infer::combine::ConstEquateRelation;
use crate::traits::Obligation;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
@ -169,3 +170,9 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
self.fields.higher_ranked_sub(a, b, self.a_is_expected)
}
}
impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> {
fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
}
}

View file

@ -10,7 +10,7 @@ pub mod util;
use rustc_hir as hir;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, Const, Ty};
use rustc_span::Span;
pub use self::FulfillmentErrorCode::*;
@ -81,6 +81,7 @@ pub enum FulfillmentErrorCode<'tcx> {
CodeSelectionError(SelectionError<'tcx>),
CodeProjectionError(MismatchedProjectionTypes<'tcx>),
CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
CodeConstEquateError(ExpectedFound<&'tcx Const<'tcx>>, TypeError<'tcx>),
CodeAmbiguity,
}

View file

@ -41,6 +41,9 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
super::CodeSubtypeError(ref a, ref b) => {
write!(f, "CodeSubtypeError({:?}, {:?})", a, b)
}
super::CodeConstEquateError(ref a, ref b) => {
write!(f, "CodeConstEquateError({:?}, {:?})", a, b)
}
super::CodeAmbiguity => write!(f, "Ambiguity"),
}
}

View file

@ -42,6 +42,8 @@ pub fn anonymize_predicate<'tcx>(
ty::Predicate::ConstEvaluatable(def_id, substs) => {
ty::Predicate::ConstEvaluatable(def_id, substs)
}
ty::Predicate::ConstEquate(c1, c2) => ty::Predicate::ConstEquate(c1, c2),
}
}
@ -187,6 +189,10 @@ impl Elaborator<'tcx> {
// Currently, we do not elaborate const-evaluatable
// predicates.
}
ty::Predicate::ConstEquate(..) => {
// Currently, we do not elaborate const-equate
// predicates.
}
ty::Predicate::RegionOutlives(..) => {
// Nothing to elaborate from `'a: 'b`.
}

View file

@ -1221,7 +1221,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints {
ObjectSafe(..) |
ClosureKind(..) |
Subtype(..) |
ConstEvaluatable(..) => continue,
ConstEvaluatable(..) |
ConstEquate(..) => continue,
};
if predicate.is_global() {
cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| {

View file

@ -1339,7 +1339,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// What mode(s) of borrowck should we run? AST? MIR? both?
/// (Also considers the `#![feature(nll)]` setting.)
pub fn borrowck_mode(&self) -> BorrowckMode {
pub fn borrowck_mode(self) -> BorrowckMode {
// Here are the main constraints we need to deal with:
//
// 1. An opts.borrowck_mode of `BorrowckMode::Migrate` is
@ -1369,6 +1369,13 @@ impl<'tcx> TyCtxt<'tcx> {
self.sess.opts.borrowck_mode
}
/// If `true`, we should use lazy normalization for constants, otherwise
/// we still evaluate them eagerly.
#[inline]
pub fn lazy_normalization(self) -> bool {
self.features().const_generics
}
#[inline]
pub fn local_crate_exports_generics(self) -> bool {
debug_assert!(self.sess.opts.share_generics());
@ -2069,24 +2076,25 @@ impl<'tcx> TyCtxt<'tcx> {
self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig }))
}
/// Given a closure signature `sig`, returns an equivalent `fn`
/// type with the same signature. Detuples and so forth -- so
/// e.g., if we have a sig with `Fn<(u32, i32)>` then you would get
/// a `fn(u32, i32)`.
/// `unsafety` determines the unsafety of the `fn` type. If you pass
/// Given a closure signature, returns an equivalent fn signature. Detuples
/// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then
/// you would get a `fn(u32, i32)`.
/// `unsafety` determines the unsafety of the fn signature. If you pass
/// `hir::Unsafety::Unsafe` in the previous example, then you would get
/// an `unsafe fn (u32, i32)`.
/// It cannot convert a closure that requires unsafe.
pub fn coerce_closure_fn_ty(self, sig: PolyFnSig<'tcx>, unsafety: hir::Unsafety) -> Ty<'tcx> {
let converted_sig = sig.map_bound(|s| {
pub fn signature_unclosure(
self,
sig: PolyFnSig<'tcx>,
unsafety: hir::Unsafety,
) -> PolyFnSig<'tcx> {
sig.map_bound(|s| {
let params_iter = match s.inputs()[0].kind {
ty::Tuple(params) => params.into_iter().map(|k| k.expect_ty()),
_ => bug!(),
};
self.mk_fn_sig(params_iter, s.output(), s.c_variadic, unsafety, abi::Abi::Rust)
});
self.mk_fn_ptr(converted_sig)
})
}
#[allow(rustc::usage_of_ty_tykind)]

View file

@ -1054,6 +1054,9 @@ pub enum Predicate<'tcx> {
/// Constant initializer must evaluate successfully.
ConstEvaluatable(DefId, SubstsRef<'tcx>),
/// Constants must be equal. The first component is the const that is expected.
ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>),
}
/// The crate outlives map is computed during typeck and contains the
@ -1172,6 +1175,9 @@ impl<'tcx> Predicate<'tcx> {
Predicate::ConstEvaluatable(def_id, const_substs) => {
Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs))
}
Predicate::ConstEquate(c1, c2) => {
Predicate::ConstEquate(c1.subst(tcx, substs), c2.subst(tcx, substs))
}
}
}
}
@ -1349,7 +1355,8 @@ impl<'tcx> Predicate<'tcx> {
| Predicate::ObjectSafe(..)
| Predicate::ClosureKind(..)
| Predicate::TypeOutlives(..)
| Predicate::ConstEvaluatable(..) => None,
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => None,
}
}
@ -1363,7 +1370,8 @@ impl<'tcx> Predicate<'tcx> {
| Predicate::WellFormed(..)
| Predicate::ObjectSafe(..)
| Predicate::ClosureKind(..)
| Predicate::ConstEvaluatable(..) => None,
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => None,
}
}
}

View file

@ -83,6 +83,11 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
}
}
ty::Array(element, _) => {
// Don't look into the len const as it doesn't affect regions
compute_components(tcx, element, out);
}
ty::Closure(_, ref substs) => {
for upvar_ty in substs.as_closure().upvar_tys() {
compute_components(tcx, upvar_ty, out);
@ -158,7 +163,6 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
ty::Opaque(..) | // OutlivesNominalType (ish)
ty::Foreign(..) | // OutlivesNominalType
ty::Str | // OutlivesScalar (ish)
ty::Array(..) | // ...
ty::Slice(..) | // ...
ty::RawPtr(..) | // ...
ty::Ref(..) | // OutlivesReference

View file

@ -2058,6 +2058,13 @@ define_print_and_forward_display! {
print_value_path(def_id, substs),
write("` can be evaluated"))
}
ty::Predicate::ConstEquate(c1, c2) => {
p!(write("the constant `"),
print(c1),
write("` equals `"),
print(c2),
write("`"))
}
}
}

View file

@ -431,6 +431,9 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>(
let t = relation.relate(&a_t, &b_t)?;
match relation.relate(&sz_a, &sz_b) {
Ok(sz) => Ok(tcx.mk_ty(ty::Array(t, sz))),
// FIXME(#72219) Implement improved diagnostics for mismatched array
// length?
Err(err) if relation.tcx().lazy_normalization() => Err(err),
Err(err) => {
// Check whether the lengths are both concrete/known values,
// but are unequal, for better diagnostics.

View file

@ -240,6 +240,7 @@ impl fmt::Debug for ty::Predicate<'tcx> {
ty::Predicate::ConstEvaluatable(def_id, substs) => {
write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs)
}
ty::Predicate::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2),
}
}
}
@ -492,6 +493,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> {
ty::Predicate::ConstEvaluatable(def_id, substs) => {
tcx.lift(&substs).map(|substs| ty::Predicate::ConstEvaluatable(def_id, substs))
}
ty::Predicate::ConstEquate(c1, c2) => {
tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::Predicate::ConstEquate(c1, c2))
}
}
}
}

View file

@ -2088,7 +2088,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ty::Closure(_, substs) => substs.as_closure().sig(),
_ => bug!(),
};
let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig, *unsafety);
let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety));
if let Err(terr) = self.eq_types(
ty_fn_ptr_from,

View file

@ -2,7 +2,7 @@ use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRe
use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, Const, Ty};
use rustc_trait_selection::traits::query::Fallible;
use crate::borrow_check::constraints::OutlivesConstraint;
@ -99,6 +99,10 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
}
}
// We don't have to worry about the equality of consts during borrow checking
// as consts always have a static lifetime.
fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {}
fn normalization() -> NormalizationStrategy {
NormalizationStrategy::Eager
}

View file

@ -277,7 +277,18 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> {
}
fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle {
if let DropFlagMode::Shallow = mode { DropStyle::Static } else { DropStyle::Open }
match mode {
DropFlagMode::Shallow => {
// Drops for the contained fields are "shallow" and "static" - they will simply call
// the field's own drop glue.
DropStyle::Static
}
DropFlagMode::Deep => {
// The top-level drop is "deep" and "open" - it will be elaborated to a drop ladder
// dropping each field contained in the value.
DropStyle::Open
}
}
}
fn get_drop_flag(&mut self, _path: Self::Path) -> Option<Operand<'tcx>> {

View file

@ -28,7 +28,8 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -
| Predicate::TypeOutlives(_)
| Predicate::WellFormed(_)
| Predicate::Projection(_)
| Predicate::ConstEvaluatable(..) => continue,
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => continue,
Predicate::ObjectSafe(_) => {
bug!("object safe predicate on function: {:#?}", predicate)
}

View file

@ -12,10 +12,15 @@ use std::fmt;
use std::convert::TryInto;
/// The value of an inserted drop flag.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum DropFlagState {
Present, // i.e., initialized
Absent, // i.e., deinitialized or "moved"
/// The tracked value is initialized and needs to be dropped when leaving its scope.
Present,
/// The tracked value is uninitialized or was moved out of and does not need to be dropped when
/// leaving its scope.
Absent,
}
impl DropFlagState {
@ -27,23 +32,42 @@ impl DropFlagState {
}
}
/// Describes how/if a value should be dropped.
#[derive(Debug)]
pub enum DropStyle {
/// The value is already dead at the drop location, no drop will be executed.
Dead,
/// The value is known to always be initialized at the drop location, drop will always be
/// executed.
Static,
/// Whether the value needs to be dropped depends on its drop flag.
Conditional,
/// An "open" drop is one where only the fields of a value are dropped.
///
/// For example, this happens when moving out of a struct field: The rest of the struct will be
/// dropped in such an "open" drop. It is also used to generate drop glue for the individual
/// components of a value, for example for dropping array elements.
Open,
}
/// Which drop flags to affect/check with an operation.
#[derive(Debug)]
pub enum DropFlagMode {
/// Only affect the top-level drop flag, not that of any contained fields.
Shallow,
/// Affect all nested drop flags in addition to the top-level one.
Deep,
}
/// Describes if unwinding is necessary and where to unwind to if a panic occurs.
#[derive(Copy, Clone, Debug)]
pub enum Unwind {
/// Unwind to this block.
To(BasicBlock),
/// Already in an unwind path, any panic will cause an abort.
InCleanup,
}
@ -74,20 +98,58 @@ impl Unwind {
}
pub trait DropElaborator<'a, 'tcx>: fmt::Debug {
/// The type representing paths that can be moved out of.
///
/// Users can move out of individual fields of a struct, such as `a.b.c`. This type is used to
/// represent such move paths. Sometimes tracking individual move paths is not necessary, in
/// which case this may be set to (for example) `()`.
type Path: Copy + fmt::Debug;
// Accessors
fn patch(&mut self) -> &mut MirPatch<'tcx>;
fn body(&self) -> &'a Body<'tcx>;
fn tcx(&self) -> TyCtxt<'tcx>;
fn param_env(&self) -> ty::ParamEnv<'tcx>;
// Drop logic
/// Returns how `path` should be dropped, given `mode`.
fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle;
/// Returns the drop flag of `path` as a MIR `Operand` (or `None` if `path` has no drop flag).
fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>>;
/// Modifies the MIR patch so that the drop flag of `path` (if any) is cleared at `location`.
///
/// If `mode` is deep, drop flags of all child paths should also be cleared by inserting
/// additional statements.
fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode);
// Subpaths
/// Returns the subpath of a field of `path` (or `None` if there is no dedicated subpath).
///
/// If this returns `None`, `field` will not get a dedicated drop flag.
fn field_subpath(&self, path: Self::Path, field: Field) -> Option<Self::Path>;
/// Returns the subpath of a dereference of `path` (or `None` if there is no dedicated subpath).
///
/// If this returns `None`, `*path` will not get a dedicated drop flag.
///
/// This is only relevant for `Box<T>`, where the contained `T` can be moved out of the box.
fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path>;
/// Returns the subpath of downcasting `path` to one of its variants.
///
/// If this returns `None`, the downcast of `path` will not get a dedicated drop flag.
fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option<Self::Path>;
/// Returns the subpath of indexing a fixed-size array `path`.
///
/// If this returns `None`, elements of `path` will not get a dedicated drop flag.
///
/// This is only relevant for array patterns, which can move out of individual array elements.
fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option<Self::Path>;
}
@ -106,6 +168,14 @@ where
unwind: Unwind,
}
/// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it.
///
/// The passed `elaborator` is used to determine what should happen at the drop terminator. It
/// decides whether the drop can be statically determined or whether it needs a dynamic drop flag,
/// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped
/// value.
///
/// When this returns, the MIR patch in the `elaborator` contains the necessary changes.
pub fn elaborate_drop<'b, 'tcx, D>(
elaborator: &mut D,
source_info: SourceInfo,
@ -346,9 +416,7 @@ where
let interior = self.tcx().mk_place_deref(self.place);
let interior_path = self.elaborator.deref_subpath(self.path);
let succ = self.succ; // FIXME(#43234)
let unwind = self.unwind;
let succ = self.box_free_block(adt, substs, succ, unwind);
let succ = self.box_free_block(adt, substs, self.succ, self.unwind);
let unwind_succ =
self.unwind.map(|unwind| self.box_free_block(adt, substs, unwind, Unwind::InCleanup));
@ -829,6 +897,8 @@ where
self.drop_flag_test_block(drop_block, succ, unwind)
}
/// Creates a block that resets the drop flag. If `mode` is deep, all children drop flags will
/// also be cleared.
fn drop_flag_reset_block(
&mut self,
mode: DropFlagMode,
@ -850,13 +920,15 @@ where
fn elaborated_drop_block(&mut self) -> BasicBlock {
debug!("elaborated_drop_block({:?})", self);
let unwind = self.unwind; // FIXME(#43234)
let succ = self.succ;
let blk = self.drop_block(succ, unwind);
let blk = self.drop_block(self.succ, self.unwind);
self.elaborate_drop(blk);
blk
}
/// Creates a block that frees the backing memory of a `Box` if its drop is required (either
/// statically or by checking its drop flag).
///
/// The contained value will not be dropped.
fn box_free_block(
&mut self,
adt: &'tcx ty::AdtDef,
@ -868,6 +940,8 @@ where
self.drop_flag_test_block(block, target, unwind)
}
/// Creates a block that frees the backing memory of a `Box` (without dropping the contained
/// value).
fn unelaborated_free_block(
&mut self,
adt: &'tcx ty::AdtDef,
@ -914,6 +988,11 @@ where
self.new_block(unwind, block)
}
/// Returns the block to jump to in order to test the drop flag and execute the drop.
///
/// Depending on the required `DropStyle`, this might be a generated block with an `if`
/// terminator (for dynamic/open drops), or it might be `on_set` or `on_unset` itself, in case
/// the drop can be statically determined.
fn drop_flag_test_block(
&mut self,
on_set: BasicBlock,

View file

@ -1277,7 +1277,8 @@ crate fn required_region_bounds(
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::RegionOutlives(..)
| ty::Predicate::ConstEvaluatable(..) => None,
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => None,
ty::Predicate::TypeOutlives(predicate) => {
// Search for a bound of the form `erased_self_ty
// : 'a`, but be wary of something like `for<'a>

View file

@ -615,6 +615,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
obligation
)
}
ty::Predicate::ConstEquate(..) => {
// Errors for `ConstEquate` predicates show up as
// `SelectionError::ConstEvalFailure`,
// not `Unimplemented`.
span_bug!(
span,
"const-equate requirement gave wrong error: `{:?}`",
obligation
)
}
}
}
@ -1092,6 +1103,15 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
)
.emit();
}
FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => {
self.report_mismatched_consts(
&error.obligation.cause,
expected_found.expected,
expected_found.found,
err.clone(),
)
.emit();
}
}
}

View file

@ -2,9 +2,11 @@ use crate::infer::{InferCtxt, TyOrConstInferVar};
use rustc_data_structures::obligation_forest::ProcessResult;
use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation};
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
use rustc_errors::ErrorReported;
use rustc_infer::traits::{TraitEngine, TraitEngineExt as _};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeFoldable};
use rustc_middle::ty::{self, Const, ToPolyTraitRef, Ty, TypeFoldable};
use std::marker::PhantomData;
use super::project;
@ -520,6 +522,68 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))),
}
}
ty::Predicate::ConstEquate(c1, c2) => {
debug!("equating consts: c1={:?} c2={:?}", c1, c2);
let stalled_on = &mut pending_obligation.stalled_on;
let mut evaluate = |c: &'tcx Const<'tcx>| {
if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val {
match self.selcx.infcx().const_eval_resolve(
obligation.param_env,
def_id,
substs,
promoted,
Some(obligation.cause.span),
) {
Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)),
Err(ErrorHandled::TooGeneric) => {
stalled_on.append(
&mut substs
.types()
.filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty))
.collect(),
);
Err(ErrorHandled::TooGeneric)
}
Err(err) => Err(err),
}
} else {
Ok(c)
}
};
match (evaluate(c1), evaluate(c2)) {
(Ok(c1), Ok(c2)) => {
match self
.selcx
.infcx()
.at(&obligation.cause, obligation.param_env)
.eq(c1, c2)
{
Ok(_) => ProcessResult::Changed(vec![]),
Err(err) => {
ProcessResult::Error(FulfillmentErrorCode::CodeConstEquateError(
ExpectedFound::new(true, c1, c2),
err,
))
}
}
}
(Err(ErrorHandled::Reported(ErrorReported)), _)
| (_, Err(ErrorHandled::Reported(ErrorReported))) => ProcessResult::Error(
CodeSelectionError(ConstEvalFailure(ErrorHandled::Reported(ErrorReported))),
),
(Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!(
obligation.cause.span(self.selcx.tcx()),
"ConstEquate: const_eval_resolve returned an unexpected error"
),
(Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
ProcessResult::Unchanged
}
}
}
}
}

View file

@ -16,8 +16,9 @@ use crate::traits::{self, Obligation, ObligationCause};
use rustc_errors::{Applicability, FatalError};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst};
use rustc_middle::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness};
use rustc_middle::ty::{Predicate, ToPredicate};
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
use rustc_span::symbol::Symbol;
use rustc_span::Span;
@ -281,7 +282,8 @@ fn predicates_reference_self(
| ty::Predicate::RegionOutlives(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::Subtype(..)
| ty::Predicate::ConstEvaluatable(..) => None,
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => None,
}
})
.collect()
@ -313,7 +315,8 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::TypeOutlives(..)
| ty::Predicate::ConstEvaluatable(..) => false,
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => false,
})
}
@ -724,51 +727,65 @@ fn contains_illegal_self_type_reference<'tcx>(
// object type, and we cannot resolve `Self as SomeOtherTrait`
// without knowing what `Self` is.
let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None;
let self_ty = tcx.types.self_param;
struct IllegalSelfTypeVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
self_ty: Ty<'tcx>,
trait_def_id: DefId,
supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>>,
}
let mut walker = ty.walk();
while let Some(arg) = walker.next() {
if arg == self_ty.into() {
return true;
}
impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> {
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
match t.kind {
ty::Param(_) => t == self.self_ty,
ty::Projection(ref data) => {
// This is a projected type `<Foo as SomeTrait>::X`.
// Special-case projections (everything else is walked normally).
if let GenericArgKind::Type(ty) = arg.unpack() {
if let ty::Projection(ref data) = ty.kind {
// This is a projected type `<Foo as SomeTrait>::X`.
// Compute supertraits of current trait lazily.
if self.supertraits.is_none() {
let trait_ref =
ty::Binder::bind(ty::TraitRef::identity(self.tcx, self.trait_def_id));
self.supertraits = Some(traits::supertraits(self.tcx, trait_ref).collect());
}
// Compute supertraits of current trait lazily.
if supertraits.is_none() {
let trait_ref = ty::Binder::bind(ty::TraitRef::identity(tcx, trait_def_id));
supertraits = Some(traits::supertraits(tcx, trait_ref).collect());
// Determine whether the trait reference `Foo as
// SomeTrait` is in fact a supertrait of the
// current trait. In that case, this type is
// legal, because the type `X` will be specified
// in the object type. Note that we can just use
// direct equality here because all of these types
// are part of the formal parameter listing, and
// hence there should be no inference variables.
let projection_trait_ref = ty::Binder::bind(data.trait_ref(self.tcx));
let is_supertrait_of_current_trait =
self.supertraits.as_ref().unwrap().contains(&projection_trait_ref);
if is_supertrait_of_current_trait {
false // do not walk contained types, do not report error, do collect $200
} else {
t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
}
}
// Determine whether the trait reference `Foo as
// SomeTrait` is in fact a supertrait of the
// current trait. In that case, this type is
// legal, because the type `X` will be specified
// in the object type. Note that we can just use
// direct equality here because all of these types
// are part of the formal parameter listing, and
// hence there should be no inference variables.
let projection_trait_ref = ty::Binder::bind(data.trait_ref(tcx));
let is_supertrait_of_current_trait =
supertraits.as_ref().unwrap().contains(&projection_trait_ref);
if is_supertrait_of_current_trait {
// Do not walk contained types, do not report error, do collect $200.
walker.skip_current_subtree();
}
// DO walk contained types, POSSIBLY reporting an error.
_ => t.super_visit_with(self), // walk contained types, if any
}
}
// Walk contained types, if any.
fn visit_const(&mut self, _c: &ty::Const<'tcx>) -> bool {
// FIXME(#72219) Look into the unevaluated constants for object safety violations.
// Do not walk substitutions of unevaluated consts, as they contain `Self`, even
// though the const expression doesn't necessary use it. Currently type variables
// inside array length expressions are forbidden, so they can't break the above
// rules.
false
}
}
false
ty.visit_with(&mut IllegalSelfTypeVisitor {
tcx,
self_ty: tcx.types.self_param,
trait_def_id,
supertraits: None,
})
}
pub fn provide(providers: &mut ty::query::Providers<'_>) {

View file

@ -388,8 +388,12 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
}
fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
let constant = constant.super_fold_with(self);
constant.eval(self.selcx.tcx(), self.param_env)
if self.selcx.tcx().lazy_normalization() {
constant
} else {
let constant = constant.super_fold_with(self);
constant.eval(self.selcx.tcx(), self.param_env)
}
}
}

View file

@ -38,11 +38,13 @@ use crate::traits::project::ProjectionCacheKeyExt;
use rustc_ast::attr;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items;
use rustc_index::bit_set::GrowableBitSet;
use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::fast_reject;
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
@ -503,9 +505,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
None,
) {
Ok(_) => Ok(EvaluatedToOk),
Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig),
Err(_) => Ok(EvaluatedToErr),
}
}
ty::Predicate::ConstEquate(c1, c2) => {
debug!("evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", c1, c2);
let evaluate = |c: &'tcx ty::Const<'tcx>| {
if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val {
self.infcx
.const_eval_resolve(
obligation.param_env,
def_id,
substs,
promoted,
Some(obligation.cause.span),
)
.map(|val| ty::Const::from_value(self.tcx(), val, c.ty))
} else {
Ok(c)
}
};
match (evaluate(c1), evaluate(c2)) {
(Ok(c1), Ok(c2)) => {
match self.infcx().at(&obligation.cause, obligation.param_env).eq(c1, c2) {
Ok(_) => Ok(EvaluatedToOk),
Err(_) => Ok(EvaluatedToErr),
}
}
(Err(ErrorHandled::Reported(ErrorReported)), _)
| (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr),
(Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!(
obligation.cause.span(self.tcx()),
"ConstEquate: const_eval_resolve returned an unexpected error"
),
(Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
Ok(EvaluatedToAmbig)
}
}
}
}
}

View file

@ -102,6 +102,10 @@ pub fn predicate_obligations<'a, 'tcx>(
wf.compute(ty);
}
}
ty::Predicate::ConstEquate(c1, c2) => {
wf.compute(c1.ty);
wf.compute(c2.ty);
}
}
wf.normalize()

View file

@ -126,9 +126,8 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::Subtype(..)
| ty::Predicate::ConstEvaluatable(..) => {
bug!("unexpected predicate {}", predicate)
}
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => bug!("unexpected predicate {}", predicate),
}
}
ChalkEnvironmentClause::TypeFromEnv(ty) => Some(
@ -192,9 +191,8 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi
Predicate::ObjectSafe(..)
| Predicate::ClosureKind(..)
| Predicate::Subtype(..)
| Predicate::ConstEvaluatable(..) => {
chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
}
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)),
}
}
}
@ -459,7 +457,8 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'
Predicate::ObjectSafe(..)
| Predicate::ClosureKind(..)
| Predicate::Subtype(..)
| Predicate::ConstEvaluatable(..) => bug!("unexpected predicate {}", &self),
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => bug!("unexpected predicate {}", &self),
}
}
}

View file

@ -100,7 +100,8 @@ fn compute_implied_outlives_bounds<'tcx>(
| ty::Predicate::Projection(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ConstEvaluatable(..) => vec![],
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => vec![],
ty::Predicate::WellFormed(subty) => {
wf_types.push(subty);

View file

@ -48,6 +48,7 @@ fn not_outlives_predicate(p: &ty::Predicate<'_>) -> bool {
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::Subtype(..)
| ty::Predicate::ConstEvaluatable(..) => true,
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => true,
}
}

View file

@ -793,7 +793,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// `unsafe fn(arg0,arg1,...) -> _`
let closure_sig = substs_a.as_closure().sig();
let unsafety = fn_ty.unsafety();
let pointer_ty = self.tcx.coerce_closure_fn_ty(closure_sig, unsafety);
let pointer_ty =
self.tcx.mk_fn_ptr(self.tcx.signature_unclosure(closure_sig, unsafety));
debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
self.unify_and(
pointer_ty,
@ -909,23 +910,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("coercion::try_find_coercion_lub({:?}, {:?})", prev_ty, new_ty);
// Special-case that coercion alone cannot handle:
// Two function item types of differing IDs or InternalSubsts.
if let (&ty::FnDef(..), &ty::FnDef(..)) = (&prev_ty.kind, &new_ty.kind) {
// Don't reify if the function types have a LUB, i.e., they
// are the same function and their parameters have a LUB.
let lub_ty = self
.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
.map(|ok| self.register_infer_ok_obligations(ok));
if lub_ty.is_ok() {
// We have a LUB of prev_ty and new_ty, just return it.
return lub_ty;
// Function items or non-capturing closures of differing IDs or InternalSubsts.
let (a_sig, b_sig) = {
let is_capturing_closure = |ty| {
if let &ty::Closure(_, substs) = ty {
substs.as_closure().upvar_tys().next().is_some()
} else {
false
}
};
if is_capturing_closure(&prev_ty.kind) || is_capturing_closure(&new_ty.kind) {
(None, None)
} else {
match (&prev_ty.kind, &new_ty.kind) {
(&ty::FnDef(..), &ty::FnDef(..)) => {
// Don't reify if the function types have a LUB, i.e., they
// are the same function and their parameters have a LUB.
match self
.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
{
// We have a LUB of prev_ty and new_ty, just return it.
Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)),
Err(_) => {
(Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx)))
}
}
}
(&ty::Closure(_, substs), &ty::FnDef(..)) => {
let b_sig = new_ty.fn_sig(self.tcx);
let a_sig = self
.tcx
.signature_unclosure(substs.as_closure().sig(), b_sig.unsafety());
(Some(a_sig), Some(b_sig))
}
(&ty::FnDef(..), &ty::Closure(_, substs)) => {
let a_sig = prev_ty.fn_sig(self.tcx);
let b_sig = self
.tcx
.signature_unclosure(substs.as_closure().sig(), a_sig.unsafety());
(Some(a_sig), Some(b_sig))
}
(&ty::Closure(_, substs_a), &ty::Closure(_, substs_b)) => (
Some(self.tcx.signature_unclosure(
substs_a.as_closure().sig(),
hir::Unsafety::Normal,
)),
Some(self.tcx.signature_unclosure(
substs_b.as_closure().sig(),
hir::Unsafety::Normal,
)),
),
_ => (None, None),
}
}
};
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
// The signature must match.
let a_sig = prev_ty.fn_sig(self.tcx);
let a_sig = self.normalize_associated_types_in(new.span, &a_sig);
let b_sig = new_ty.fn_sig(self.tcx);
let b_sig = self.normalize_associated_types_in(new.span, &b_sig);
let sig = self
.at(cause, self.param_env)
@ -935,17 +976,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Reify both sides and return the reified fn pointer type.
let fn_ptr = self.tcx.mk_fn_ptr(sig);
for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) {
// The only adjustment that can produce an fn item is
// `NeverToAny`, so this should always be valid.
let prev_adjustment = match prev_ty.kind {
ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())),
ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
_ => unreachable!(),
};
let next_adjustment = match new_ty.kind {
ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())),
ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
_ => unreachable!(),
};
for expr in exprs.iter().map(|e| e.as_coercion_site()) {
self.apply_adjustments(
expr,
vec![Adjustment {
kind: Adjust::Pointer(PointerCast::ReifyFnPointer),
target: fn_ptr,
}],
vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }],
);
}
self.apply_adjustments(new, vec![Adjustment { kind: next_adjustment, target: fn_ptr }]);
return Ok(fn_ptr);
}

View file

@ -810,7 +810,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::TypeOutlives(..)
| ty::Predicate::ConstEvaluatable(..) => None,
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => None,
});
self.elaborate_bounds(bounds, |this, poly_trait_ref, item| {

View file

@ -1648,6 +1648,16 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId,
r.super_visit_with(self)
}
fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
if let ty::ConstKind::Unevaluated(..) = c.val {
// FIXME(#72219) We currenctly don't detect lifetimes within substs
// which would violate this check. Even though the particular substitution is not used
// within the const, this should still be fixed.
return false;
}
c.super_visit_with(self)
}
}
let prohibit_opaque = match item.kind {
@ -3858,6 +3868,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::Predicate::WellFormed(..) => None,
ty::Predicate::ObjectSafe(..) => None,
ty::Predicate::ConstEvaluatable(..) => None,
ty::Predicate::ConstEquate(..) => None,
// N.B., this predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
// `ClosureType` represents some `Closure`. It can't

View file

@ -1164,7 +1164,8 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
let parent_id = tcx.hir().get_parent_item(hir_id);
Some(tcx.hir().local_def_id(parent_id).to_def_id())
}
// FIXME(#43408) enable this always when we get lazy normalization.
// FIXME(#43408) always enable this once `lazy_normalization` is
// stable enough and does not need a feature gate anymore.
Node::AnonConst(_) => {
let parent_id = tcx.hir().get_parent_item(hir_id);
let parent_def_id = tcx.hir().local_def_id(parent_id);
@ -1172,7 +1173,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
// HACK(eddyb) this provides the correct generics when
// `feature(const_generics)` is enabled, so that const expressions
// used with const generics, e.g. `Foo<{N+1}>`, can work at all.
if tcx.features().const_generics {
if tcx.lazy_normalization() {
Some(parent_def_id.to_def_id())
} else {
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));

View file

@ -413,6 +413,7 @@ fn trait_predicate_kind<'tcx>(
| ty::Predicate::Subtype(_)
| ty::Predicate::ObjectSafe(_)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::ConstEvaluatable(..) => None,
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => None,
}
}

View file

@ -58,7 +58,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::Subtype(..)
| ty::Predicate::ConstEvaluatable(..) => (),
| ty::Predicate::ConstEvaluatable(..)
| ty::Predicate::ConstEquate(..) => (),
}
}

View file

@ -493,7 +493,8 @@ impl<'a> Clean<Option<WherePredicate>> for ty::Predicate<'a> {
Predicate::WellFormed(..)
| Predicate::ObjectSafe(..)
| Predicate::ClosureKind(..)
| Predicate::ConstEvaluatable(..) => panic!("not user writable"),
| Predicate::ConstEvaluatable(..)
| Predicate::ConstEquate(..) => panic!("not user writable"),
}
}
}

View file

@ -0,0 +1,39 @@
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
// We shouldn't coerce capturing closure to a function
let cap = 0;
let _ = match "+" {
"+" => add,
"-" => |a, b| (a - b + cap) as i32,
_ => unimplemented!(),
};
//~^^^ ERROR `match` arms have incompatible types
// We shouldn't coerce capturing closure to a non-capturing closure
let _ = match "+" {
"+" => |a, b| (a + b) as i32,
"-" => |a, b| (a - b + cap) as i32,
_ => unimplemented!(),
};
//~^^^ ERROR `match` arms have incompatible types
// We shouldn't coerce non-capturing closure to a capturing closure
let _ = match "+" {
"+" => |a, b| (a + b + cap) as i32,
"-" => |a, b| (a - b) as i32,
_ => unimplemented!(),
};
//~^^^ ERROR `match` arms have incompatible types
// We shouldn't coerce capturing closure to a capturing closure
let _ = match "+" {
"+" => |a, b| (a + b + cap) as i32,
"-" => |a, b| (a - b + cap) as i32,
_ => unimplemented!(),
};
//~^^^ ERROR `match` arms have incompatible types
}

View file

@ -0,0 +1,73 @@
error[E0308]: `match` arms have incompatible types
--> $DIR/closure_cap_coerce_many_fail.rs:9:16
|
LL | let _ = match "+" {
| _____________-
LL | | "+" => add,
| | --- this is found to be of type `fn(i32, i32) -> i32 {add}`
LL | | "-" => |a, b| (a - b + cap) as i32,
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found closure
LL | | _ => unimplemented!(),
LL | | };
| |_____- `match` arms have incompatible types
|
= note: expected type `fn(i32, i32) -> i32 {add}`
found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:9:16: 9:43 cap:_]`
error[E0308]: `match` arms have incompatible types
--> $DIR/closure_cap_coerce_many_fail.rs:18:16
|
LL | let _ = match "+" {
| _____________-
LL | | "+" => |a, b| (a + b) as i32,
| | --------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]`
LL | | "-" => |a, b| (a - b + cap) as i32,
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
LL | | _ => unimplemented!(),
LL | | };
| |_____- `match` arms have incompatible types
|
= note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]`
found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:18:16: 18:43 cap:_]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
error[E0308]: `match` arms have incompatible types
--> $DIR/closure_cap_coerce_many_fail.rs:27:16
|
LL | let _ = match "+" {
| _____________-
LL | | "+" => |a, b| (a + b + cap) as i32,
| | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43 cap:_]`
LL | | "-" => |a, b| (a - b) as i32,
| | ^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
LL | | _ => unimplemented!(),
LL | | };
| |_____- `match` arms have incompatible types
|
= note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43 cap:_]`
found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:27:16: 27:37]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
error[E0308]: `match` arms have incompatible types
--> $DIR/closure_cap_coerce_many_fail.rs:35:16
|
LL | let _ = match "+" {
| _____________-
LL | | "+" => |a, b| (a + b + cap) as i32,
| | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43 cap:_]`
LL | | "-" => |a, b| (a - b + cap) as i32,
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
LL | | _ => unimplemented!(),
LL | | };
| |_____- `match` arms have incompatible types
|
= note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43 cap:_]`
found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:35:16: 35:43 cap:_]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -0,0 +1,166 @@
// check-pass
// Ensure non-capturing Closure passes CoerceMany.
fn foo(x: usize) -> usize {
0
}
fn bar(x: usize) -> usize {
1
}
fn main() {
// One FnDef and one non-capturing Closure
let _ = match 0 {
0 => foo,
2 => |a| 2,
_ => unimplemented!(),
};
let _ = match 0 {
2 => |a| 2,
0 => foo,
_ => unimplemented!(),
};
let _ = [foo, |a| 2];
let _ = [|a| 2, foo];
// Two FnDefs and one non-capturing Closure
let _ = match 0 {
0 => foo,
1 => bar,
2 => |a| 2,
_ => unimplemented!(),
};
let _ = match 0 {
0 => foo,
2 => |a| 2,
1 => bar,
_ => unimplemented!(),
};
let _ = match 0 {
2 => |a| 2,
0 => foo,
1 => bar,
_ => unimplemented!(),
};
let _ = [foo, bar, |a| 2];
let _ = [foo, |a| 2, bar];
let _ = [|a| 2, foo, bar];
// One FnDef and two non-capturing Closures
let _ = match 0 {
0 => foo,
1 => |a| 1,
2 => |a| 2,
_ => unimplemented!(),
};
let _ = match 0 {
1 => |a| 1,
0 => foo,
2 => |a| 2,
_ => unimplemented!(),
};
let _ = match 0 {
1 => |a| 1,
2 => |a| 2,
0 => foo,
_ => unimplemented!(),
};
let _ = [foo, |a| 1, |a| 2];
let _ = [|a| 1, foo, |a| 2];
let _ = [|a| 1, |a| 2, foo];
// Three non-capturing Closures
let _ = match 0 {
0 => |a: usize| 0,
1 => |a| 1,
2 => |a| 2,
_ => unimplemented!(),
};
let _ = [|a: usize| 0, |a| 1, |a| 2];
// Three non-capturing Closures variable
let clo0 = |a: usize| 0;
let clo1 = |a| 1;
let clo2 = |a| 2;
let _ = match 0 {
0 => clo0,
1 => clo1,
2 => clo2,
_ => unimplemented!(),
};
let clo0 = |a: usize| 0;
let clo1 = |a| 1;
let clo2 = |a| 2;
let _ = [clo0, clo1, clo2];
// --- Function pointer related part
// Closure is not in a variable
type FnPointer = fn(usize) -> usize;
let _ = match 0 {
0 => foo as FnPointer,
2 => |a| 2,
_ => unimplemented!(),
};
let _ = match 0 {
2 => |a| 2,
0 => foo as FnPointer,
_ => unimplemented!(),
};
let _ = [foo as FnPointer, |a| 2];
let _ = [|a| 2, foo as FnPointer];
let _ = [foo, bar, |x| x];
let _ = [foo as FnPointer, bar, |x| x];
let _ = [foo, bar as FnPointer, |x| x];
let _ = [foo, bar, (|x| x) as FnPointer];
let _ = [foo as FnPointer, bar as FnPointer, |x| x];
// Closure is in a variable
let x = |a| 2;
let _ = match 0 {
0 => foo as FnPointer,
2 => x,
_ => unimplemented!(),
};
let x = |a| 2;
let _ = match 0 {
2 => x,
0 => foo as FnPointer,
_ => unimplemented!(),
};
let x = |a| 2;
let _ = [foo as FnPointer, x];
let _ = [x, foo as FnPointer];
let x = |a| 2;
let _ = [foo, bar, x];
let x: FnPointer = |a| 2;
let _ = [foo, bar, x];
let x = |a| 2;
let _ = [foo, bar as FnPointer, x];
let x = |a| 2;
let _ = [foo as FnPointer, bar, x];
let x = |a| 2;
let _ = [foo as FnPointer, bar as FnPointer, x];
}

View file

@ -0,0 +1,59 @@
// run-pass
// Ensure non-capturing Closure passing CoerceMany work correctly.
fn foo(_: usize) -> usize {
0
}
fn bar(_: usize) -> usize {
1
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
// Coerce result check
type FnPointer = fn(usize) -> usize;
let c = |x| x;
let c_pointer: FnPointer = c;
assert_eq!(c_pointer(42), 42);
let f = match 0 {
0 => foo,
1 => |_| 1,
_ => unimplemented!(),
};
assert_eq!(f(42), 0);
let f = match 2 {
2 => |_| 2,
0 => foo,
_ => unimplemented!(),
};
assert_eq!(f(42), 2);
let f = match 1 {
0 => foo,
1 => bar,
2 => |_| 2,
_ => unimplemented!(),
};
assert_eq!(f(42), 1);
let clo0 = |_: usize| 0;
let clo1 = |_| 1;
let clo2 = |_| 2;
let f = match 0 {
0 => clo0,
1 => clo1,
2 => clo2,
_ => unimplemented!(),
};
assert_eq!(f(42), 0);
let funcs = [add, |a, b| (a - b) as i32];
assert_eq!([funcs[0](5, 5), funcs[1](5, 5)], [10, 0]);
}

View file

@ -0,0 +1,22 @@
// Ensure we get unsafe function after coercion
unsafe fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
// We can coerce non-capturing closure to unsafe function
let foo = match "+" {
"+" => add,
"-" => |a, b| (a - b) as i32,
_ => unimplemented!(),
};
let result: i32 = foo(5, 5); //~ ERROR call to unsafe function
// We can coerce unsafe function to non-capturing closure
let foo = match "+" {
"-" => |a, b| (a - b) as i32,
"+" => add,
_ => unimplemented!(),
};
let result: i32 = foo(5, 5); //~ ERROR call to unsafe function
}

View file

@ -0,0 +1,19 @@
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
--> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:12:23
|
LL | let result: i32 = foo(5, 5);
| ^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
--> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:21:23
|
LL | let result: i32 = foo(5, 5);
| ^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0133`.

View file

@ -0,0 +1,23 @@
// run-pass
// Ensure we get correct unsafe function after coercion
unsafe fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
// We can coerce non-capturing closure to unsafe function
let foo = match "+" {
"+" => add,
"-" => |a, b| (a - b) as i32,
_ => unimplemented!(),
};
assert_eq!(unsafe { foo(5, 5) }, 10);
// We can coerce unsafe function to non-capturing closure
let foo = match "-" {
"-" => |a, b| (a - b) as i32,
"+" => add,
_ => unimplemented!(),
};
assert_eq!(unsafe { foo(5, 5) }, 0);
}

View file

@ -0,0 +1,9 @@
// check-pass
fn main() {
let _: i32 = (match "" {
"+" => ::std::ops::Add::add,
"-" => ::std::ops::Sub::sub,
"<" => |a,b| (a < b) as i32,
_ => unimplemented!(),
})(5, 5);
}

View file

@ -0,0 +1,14 @@
// check-pass
fn useful(i: usize) -> usize {
i
}
fn useful2(i: usize) -> usize {
i
}
fn main() {
for f in &[useful, useful2, |x| x] {
println!("{}", f(6));
}
}

View file

@ -13,8 +13,8 @@ error[E0308]: mismatched types
LL | x = Const::<{ [4] }> {};
| ^^^^^^^^^^^^^^^^^^^ expected `3usize`, found `4usize`
|
= note: expected struct `Const<[3usize]>`
found struct `Const<[4usize]>`
= note: expected type `[3usize]`
found type `[4usize]`
error: aborting due to previous error; 1 warning emitted

View file

@ -11,12 +11,10 @@ error[E0308]: mismatched types
--> $DIR/fn-const-param-infer.rs:16:31
|
LL | let _: Checked<not_one> = Checked::<not_two>;
| ---------------- ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}`
| |
| expected due to this
| ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}`
|
= note: expected struct `Checked<{not_one as fn(usize) -> bool}>`
found struct `Checked<{not_two as fn(usize) -> bool}>`
= note: expected type `{not_one as fn(usize) -> bool}`
found type `{not_two as fn(usize) -> bool}`
error[E0308]: mismatched types
--> $DIR/fn-const-param-infer.rs:20:24
@ -37,12 +35,10 @@ error[E0308]: mismatched types
--> $DIR/fn-const-param-infer.rs:25:40
|
LL | let _: Checked<{generic::<u32>}> = Checked::<{generic::<u16>}>;
| ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic::<u32> as fn(usize) -> bool}`, found `{generic::<u16> as fn(usize) -> bool}`
| |
| expected due to this
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic::<u32> as fn(usize) -> bool}`, found `{generic::<u16> as fn(usize) -> bool}`
|
= note: expected struct `Checked<{generic::<u32> as fn(usize) -> bool}>`
found struct `Checked<{generic::<u16> as fn(usize) -> bool}>`
= note: expected type `{generic::<u32> as fn(usize) -> bool}`
found type `{generic::<u16> as fn(usize) -> bool}`
error: aborting due to 4 previous errors; 1 warning emitted

View file

@ -0,0 +1,24 @@
// check-pass
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete
trait Foo {}
impl<const N: usize> Foo for [(); N]
where
Self:FooImpl<{N==0}>
{}
trait FooImpl<const IS_ZERO: bool>{}
impl FooImpl<true> for [(); 0] {}
impl<const N:usize> FooImpl<false> for [();N] {}
fn foo(_: impl Foo) {}
fn main() {
foo([]);
foo([()]);
}

View file

@ -0,0 +1,11 @@
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-61935.rs:3:12
|
LL | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
warning: 1 warning emitted

View file

@ -16,8 +16,7 @@ struct ArrayHolder<const X: usize>([u32; X]);
impl<const X: usize> ArrayHolder<X> {
pub const fn new() -> Self {
ArrayHolder([0; Self::SIZE])
//~^ ERROR: mismatched types
//~| ERROR constant expression depends on a generic parameter
//~^ ERROR constant expression depends on a generic parameter
}
}

View file

@ -1,12 +1,3 @@
error[E0308]: mismatched types
--> $DIR/issue-62504.rs:18:21
|
LL | ArrayHolder([0; Self::SIZE])
| ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`
|
= note: expected array `[u32; X]`
found array `[u32; _]`
error: constant expression depends on a generic parameter
--> $DIR/issue-62504.rs:18:25
|
@ -15,6 +6,5 @@ LL | ArrayHolder([0; Self::SIZE])
|
= note: this may fail depending on what value the parameter takes
error: aborting due to 2 previous errors
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.

View file

@ -1,5 +1,6 @@
#![allow(incomplete_features, dead_code, unconditional_recursion)]
#![feature(const_generics)]
#![feature(lazy_normalization_consts)]
fn fact<const N: usize>() {
fact::<{ N - 1 }>();

View file

@ -1,5 +1,5 @@
error: constant expression depends on a generic parameter
--> $DIR/issue-66205.rs:5:12
--> $DIR/issue-66205.rs:6:12
|
LL | fact::<{ N - 1 }>();
| ^^^^^^^^^

View file

@ -0,0 +1,32 @@
// check-pass
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete
trait Baz {
type Quaks;
}
impl Baz for u8 {
type Quaks = [u16; 3];
}
trait Bar {}
impl Bar for [u16; 3] {}
impl Bar for [[u16; 3]; 2] {}
trait Foo
where
[<u8 as Baz>::Quaks; 2]: Bar,
<u8 as Baz>::Quaks: Bar,
{
}
struct FooImpl;
impl Foo for FooImpl {}
fn f(_: impl Foo) {}
fn main() {
f(FooImpl)
}

View file

@ -0,0 +1,11 @@
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-67185-1.rs:3:12
|
LL | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
warning: 1 warning emitted

View file

@ -0,0 +1,35 @@
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete
trait Baz {
type Quaks;
}
impl Baz for u8 {
type Quaks = [u16; 3];
}
trait Bar {}
impl Bar for [u16; 4] {}
impl Bar for [[u16; 3]; 3] {}
trait Foo //~ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277]
//~^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277]
where
[<u8 as Baz>::Quaks; 2]: Bar,
<u8 as Baz>::Quaks: Bar,
{
}
struct FooImpl;
impl Foo for FooImpl {}
//~^ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277]
//~^^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277]
fn f(_: impl Foo) {}
//~^ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277]
//~^^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277]
fn main() {
f(FooImpl)
}

View file

@ -0,0 +1,112 @@
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-67185-2.rs:1:12
|
LL | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
--> $DIR/issue-67185-2.rs:15:1
|
LL | / trait Foo
LL | |
LL | | where
LL | | [<u8 as Baz>::Quaks; 2]: Bar,
LL | | <u8 as Baz>::Quaks: Bar,
LL | | {
LL | | }
| |_^ the trait `Bar` is not implemented for `[u16; 3]`
|
= help: the following implementations were found:
<[[u16; 3]; 3] as Bar>
<[u16; 4] as Bar>
= help: see issue #48214
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
--> $DIR/issue-67185-2.rs:15:1
|
LL | / trait Foo
LL | |
LL | | where
LL | | [<u8 as Baz>::Quaks; 2]: Bar,
LL | | <u8 as Baz>::Quaks: Bar,
LL | | {
LL | | }
| |_^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
|
= help: the following implementations were found:
<[[u16; 3]; 3] as Bar>
<[u16; 4] as Bar>
= help: see issue #48214
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
--> $DIR/issue-67185-2.rs:25:6
|
LL | trait Foo
| --- required by a bound in this
...
LL | <u8 as Baz>::Quaks: Bar,
| --- required by this bound in `Foo`
...
LL | impl Foo for FooImpl {}
| ^^^ the trait `Bar` is not implemented for `[u16; 3]`
|
= help: the following implementations were found:
<[[u16; 3]; 3] as Bar>
<[u16; 4] as Bar>
error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
--> $DIR/issue-67185-2.rs:25:6
|
LL | trait Foo
| --- required by a bound in this
...
LL | [<u8 as Baz>::Quaks; 2]: Bar,
| --- required by this bound in `Foo`
...
LL | impl Foo for FooImpl {}
| ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
|
= help: the following implementations were found:
<[[u16; 3]; 3] as Bar>
<[u16; 4] as Bar>
error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
--> $DIR/issue-67185-2.rs:29:14
|
LL | trait Foo
| --- required by a bound in this
...
LL | [<u8 as Baz>::Quaks; 2]: Bar,
| --- required by this bound in `Foo`
...
LL | fn f(_: impl Foo) {}
| ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
|
= help: the following implementations were found:
<[[u16; 3]; 3] as Bar>
<[u16; 4] as Bar>
error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
--> $DIR/issue-67185-2.rs:29:14
|
LL | trait Foo
| --- required by a bound in this
...
LL | <u8 as Baz>::Quaks: Bar,
| --- required by this bound in `Foo`
...
LL | fn f(_: impl Foo) {}
| ^^^ the trait `Bar` is not implemented for `[u16; 3]`
|
= help: the following implementations were found:
<[[u16; 3]; 3] as Bar>
<[u16; 4] as Bar>
error: aborting due to 6 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0277`.

View file

@ -1,18 +0,0 @@
#![feature(const_generics)]
#![allow(incomplete_features)]
trait Bar<O> {}
impl<O> Bar<O> for [u8; O] {}
//~^ ERROR expected value, found type parameter `O`
struct Foo<const O: usize> {}
impl<const O: usize> Foo<O>
where
[u8; O]: Bar<[(); O]>,
{
fn foo() {}
}
fn main() {
Foo::foo();
}

View file

@ -1,14 +0,0 @@
error[E0423]: expected value, found type parameter `O`
--> $DIR/issue-69654.rs:5:25
|
LL | impl<O> Bar<O> for [u8; O] {}
| ^ help: a tuple variant with a similar name exists: `Ok`
|
::: $SRC_DIR/libcore/result.rs:LL:COL
|
LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
| --------------------------------------------------- similarly named tuple variant `Ok` defined here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0423`.

View file

@ -0,0 +1,19 @@
// run-pass
#![feature(const_generics)]
#![allow(incomplete_features)]
trait Foo {}
impl<const N: usize> Foo for [(); N] where Self: FooImpl<{ N == 0 }> {}
trait FooImpl<const IS_ZERO: bool> {}
impl FooImpl<{ 0u8 == 0u8 }> for [(); 0] {}
impl<const N: usize> FooImpl<{ 0u8 != 0u8 }> for [(); N] {}
fn foo<T: Foo>(_: T) {}
fn main() {
foo([]);
foo([()]);
}

View file

@ -0,0 +1,8 @@
// check-pass
#![allow(incomplete_features)]
#![feature(const_generics)]
pub trait Foo<const B: bool> {}
pub fn bar<T: Foo<{ true }>>() {}
fn main() {}

View file

@ -11,12 +11,10 @@ error[E0308]: mismatched types
--> $DIR/raw-ptr-const-param.rs:7:40
|
LL | let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>;
| ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}`
| |
| expected due to this
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}`
|
= note: expected struct `Const<{0xf as *const u32}>`
found struct `Const<{0xa as *const u32}>`
= note: expected type `{0xf as *const u32}`
found type `{0xa as *const u32}`
error: aborting due to previous error; 1 warning emitted

View file

@ -11,12 +11,10 @@ error[E0308]: mismatched types
--> $DIR/types-mismatch-const-args.rs:13:41
|
LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData };
| -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2u32`, found `4u32`
| |
| expected due to this
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2u32`, found `4u32`
|
= note: expected struct `A<'_, _, 2u32, _>`
found struct `A<'_, _, 4u32, _>`
= note: expected type `2u32`
found type `4u32`
error[E0308]: mismatched types
--> $DIR/types-mismatch-const-args.rs:15:41
@ -26,8 +24,8 @@ LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data
| |
| expected due to this
|
= note: expected struct `A<'a, u16, _, _>`
found struct `A<'b, u32, _, _>`
= note: expected struct `A<'a, u16, {2u32}, {3u32}>`
found struct `A<'b, u32, {2u32}, {3u32}>`
error: aborting due to 2 previous errors; 1 warning emitted

View file

@ -10,7 +10,7 @@ fn closure_from_match() {
2 => |c| c - 1,
_ => |c| c - 1
};
//~^^^ ERROR `match` arms have incompatible types
//~^^^^ ERROR type annotations needed
}
fn main() { }

View file

@ -11,24 +11,13 @@ LL | x = |c| c + 1;
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
error[E0308]: `match` arms have incompatible types
--> $DIR/issue-24036.rs:10:14
error[E0282]: type annotations needed
--> $DIR/issue-24036.rs:9:15
|
LL | let x = match 1usize {
| _____________-
LL | | 1 => |c| c + 1,
| | --------- this is found to be of type `[closure@$DIR/issue-24036.rs:9:14: 9:23]`
LL | | 2 => |c| c - 1,
| | ^^^^^^^^^ expected closure, found a different closure
LL | | _ => |c| c - 1
LL | | };
| |_____- `match` arms have incompatible types
|
= note: expected type `[closure@$DIR/issue-24036.rs:9:14: 9:23]`
found closure `[closure@$DIR/issue-24036.rs:10:14: 10:23]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
LL | 1 => |c| c + 1,
| ^ consider giving this closure parameter a type
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.
Some errors have detailed explanations: E0282, E0308.
For more information about an error, try `rustc --explain E0282`.

View file

@ -25,7 +25,7 @@ error: non-defining opaque type use in defining scope
LL | fn concrete_const() -> OneConst<{123}> {
| ^^^^^^^^^^^^^^^
|
note: used non-generic constant `123usize` for generic parameter
note: used non-generic constant `{123}` for generic parameter
--> $DIR/generic_nondefining_use.rs:10:21
|
LL | type OneConst<const X: usize> = impl Debug;

View file

@ -18,6 +18,7 @@ use std::thread;
use std::time::Duration;
const REMOTE_ADDR_ENV: &str = "TEST_DEVICE_ADDR";
const DEFAULT_ADDR: &str = "127.0.0.1:12345";
macro_rules! t {
($e:expr) => {
@ -30,8 +31,12 @@ macro_rules! t {
fn main() {
let mut args = env::args().skip(1);
let next = args.next();
if next.is_none() {
return help();
}
match &args.next().unwrap()[..] {
match &next.unwrap()[..] {
"spawn-emulator" => spawn_emulator(
&args.next().unwrap(),
Path::new(&args.next().unwrap()),
@ -40,12 +45,16 @@ fn main() {
),
"push" => push(Path::new(&args.next().unwrap())),
"run" => run(args.next().unwrap(), args.collect()),
cmd => panic!("unknown command: {}", cmd),
"help" | "-h" | "--help" => help(),
cmd => {
println!("unknown command: {}", cmd);
help();
}
}
}
fn spawn_emulator(target: &str, server: &Path, tmpdir: &Path, rootfs: Option<PathBuf>) {
let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string());
let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());
if env::var(REMOTE_ADDR_ENV).is_ok() {
println!("Connecting to remote device {} ...", device_address);
@ -172,7 +181,7 @@ fn start_qemu_emulator(target: &str, rootfs: &Path, server: &Path, tmpdir: &Path
}
fn push(path: &Path) {
let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string());
let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());
let client = t!(TcpStream::connect(device_address));
let mut client = BufWriter::new(client);
t!(client.write_all(b"push"));
@ -189,7 +198,7 @@ fn push(path: &Path) {
}
fn run(files: String, args: Vec<String>) {
let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string());
let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());
let client = t!(TcpStream::connect(device_address));
let mut client = BufWriter::new(client);
t!(client.write_all(b"run "));
@ -284,3 +293,40 @@ fn send(path: &Path, dst: &mut dyn Write) {
t!(dst.write_all(&[(amt >> 24) as u8, (amt >> 16) as u8, (amt >> 8) as u8, (amt >> 0) as u8,]));
t!(io::copy(&mut file, dst));
}
fn help() {
println!(
"
Usage: {0} <command> [<args>]
Sub-commands:
spawn-emulator <target> <server> <tmpdir> [rootfs] See below
push <path> Copy <path> to emulator
run <files> [args...] Run program on emulator
help Display help message
Spawning an emulator:
For Android <target>s, adb will push the <server>, set up TCP forwarding and run
the <server>. Otherwise qemu emulates the target using a rootfs image created in
<tmpdir> and generated from <rootfs> plus the <server> executable.
If {1} is set in the environment, this step is skipped.
Pushing a path to a running emulator:
A running emulator or adb device is connected to at the IP address and port in
the {1} environment variable or {2} if this isn't
specified. The file at <path> is sent to this target.
Executing commands on a running emulator:
First the target emulator/adb session is connected to as for pushing files. Next
the colon separated list of <files> is pushed to the target. Finally, the first
file in <files> is executed in the emulator, preserving the current environment.
That command's status code is returned.
",
env::args().next().unwrap(),
REMOTE_ADDR_ENV,
DEFAULT_ADDR
);
}