Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions compiler/rustc_builtin_macros/src/deriving/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,15 @@ fn default_struct_substructure(
variant_data: &VariantData,
type_ident: Ident,
) -> BlockOrExpr {
// FIXME: This is currently needed to avoid a regression with attribute macros after
// derives that incorrectly rely on the generated code using `Unit {}` instead of `Unit`.
// See https://github.com/rust-lang/rust/issues/164078.
if variant_data.fields().is_empty() {
return BlockOrExpr::new_expr(cx.expr_struct_ident(trait_span, type_ident, ThinVec::new()));
}

let expr = match variant_data {
// This is currently unreachable due to the above hack.
VariantData::Unit(_) => cx.expr_ident(trait_span, type_ident),
VariantData::Tuple(fields, _) => {
let exprs = fields
Expand Down
122 changes: 65 additions & 57 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ use rustc_hir_analysis::check::potentially_plural_count;
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, ResolvedStructPath};
use rustc_index::IndexVec;
use rustc_infer::infer::{
BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TypeTrace, relate,
BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TypeTrace, relate,
};
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Unnormalized};
Expand Down Expand Up @@ -292,22 +292,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected_input_tys
.into_iter()
.zip(formal_input_tys)
// if the expected input type is structurally equal to the formal input type,
// i.e. we've only changed some inference variables around, keep the formal
// input ty as the expected input ty. Usually fudging helps because it gains
// information from a callsite of a function. However, Fudging also sometimes
// loses information, when the original, formal, input type had constraints on it,
// and fudging replaces all inference variables with fresh ones, those constraints
// are discarded. This check makes sure we only keep fudging output if structural
// changes were made to the type. If all that was changed were some typevars,
// we go back to the unfudged formal input type.
.map(|(expected_input_ty, formal_input_ty)| {
if same_type_modulo_vars(tcx, expected_input_ty, *formal_input_ty) {
// if they're the same, fall back to the formal input type
*formal_input_ty
} else {
expected_input_ty
}
only_keep_structural_changes(
&self.infcx,
*formal_input_ty,
expected_input_ty,
)
})
.collect()
}))
Expand Down Expand Up @@ -3572,50 +3562,63 @@ enum SuggestionText {
DidYouMean,
}

fn same_type_modulo_vars<'tcx>(tcx: TyCtxt<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
struct SameModuloVars<'tcx> {
tcx: TyCtxt<'tcx>,
/// Postprocessing step of fudging: after fudging, any changes from the formal input type to the
/// expected input type are only kept, if they result in structural changes. If the only change is
/// one inference variable changing to another (fresh) inference variable, the formal one is kept,
/// not the fresh one. This avoids cases in which those fresh inference variables are *less*
/// constrained than the original formal ones, and making them fresh has no benefit.
fn only_keep_structural_changes<'tcx>(
infcx: &InferCtxt<'tcx>,
formal: Ty<'tcx>,
expected: Ty<'tcx>,
) -> Ty<'tcx> {
struct OnlyKeepStructuralChanges<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
}
impl<'tcx> TypeRelation<TyCtxt<'tcx>> for SameModuloVars<'tcx> {
impl<'tcx> TypeRelation<TyCtxt<'tcx>> for OnlyKeepStructuralChanges<'_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
self.infcx.tcx
}

fn relate_ty_args(
&mut self,
a_ty: Ty<'tcx>,
_b_ty: Ty<'tcx>,
_ty_def_id: DefId,
a_args: ty::GenericArgsRef<'tcx>,
b_args: ty::GenericArgsRef<'tcx>,
_mk: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>,
formal_ty: Ty<'tcx>,
_expected_ty: Ty<'tcx>,
_def_id: DefId,
formal_args: ty::GenericArgsRef<'tcx>,
expected_args: ty::GenericArgsRef<'tcx>,
mk: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>,
) -> RelateResult<'tcx, Ty<'tcx>> {
relate::relate_args_invariantly(self, a_args, b_args)?;
Ok(a_ty)
let new_args = relate::relate_args_invariantly(self, formal_args, expected_args)?;

if new_args == formal_args { Ok(formal_ty) } else { Ok(mk(new_args)) }
}

fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
&mut self,
_variance: ty::Variance,
_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
a: T,
b: T,
formal: T,
expected: T,
) -> RelateResult<'tcx, T> {
self.relate(a, b)
self.relate(formal, expected)
}

fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
if a == b {
return Ok(a);
fn tys(&mut self, formal: Ty<'tcx>, expected: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
if formal == expected {
return Ok(formal);
}

match (a.kind(), b.kind()) {
match (formal.kind(), expected.kind()) {
// If both are an infer var, keep the formal one.
(&ty::Infer(ty::InferTy::TyVar(_)), &ty::Infer(ty::InferTy::TyVar(_)))
| (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Infer(ty::InferTy::FloatVar(_)))
| (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_))) => Ok(a),
(&ty::Infer(_), _) | (_, &ty::Infer(_)) => Err(TypeError::Mismatch),
| (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_))) => {
Ok(formal)
}
(&ty::Infer(_), _) | (_, &ty::Infer(_)) => Ok(expected),
(&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.cx(), guar)),
_ => relate::structurally_relate_tys(self, a, b),
_ => relate::structurally_relate_tys(self, formal, expected),
}
}

Expand All @@ -3629,42 +3632,47 @@ fn same_type_modulo_vars<'tcx>(tcx: TyCtxt<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> b

fn consts(
&mut self,
mut a: ty::Const<'tcx>,
mut b: ty::Const<'tcx>,
mut formal: ty::Const<'tcx>,
mut expected: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
if a == b {
return Ok(a);
if formal == expected {
return Ok(formal);
}

// Avoid ICEs when in gce, and `structurally_relate_consts`
// turns a non-infer const into an infer const
if self.tcx.features().generic_const_exprs() {
a = self.tcx.expand_abstract_consts(a);
b = self.tcx.expand_abstract_consts(b);
if self.cx().features().generic_const_exprs() {
formal = self.cx().expand_abstract_consts(formal);
expected = self.cx().expand_abstract_consts(expected);
}

match (a.kind(), b.kind()) {
(ty::ConstKind::Infer(_), ty::ConstKind::Infer(_)) => return Ok(a),
(ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
return Err(TypeError::ConstMismatch(ExpectedFound::new(a, b)));
}
match (formal.kind(), expected.kind()) {
// If both are an infer var, keep the formal one.
(ty::ConstKind::Infer(_), ty::ConstKind::Infer(_)) => return Ok(formal),
(ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => return Ok(formal),

_ => {}
}

relate::structurally_relate_consts(self, a, b)
relate::structurally_relate_consts(self, formal, expected)
}

fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
b: ty::Binder<'tcx, T>,
formal: ty::Binder<'tcx, T>,
expected: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<TyCtxt<'tcx>>,
{
Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
Ok(formal.rebind(self.relate(formal.skip_binder(), expected.skip_binder())?))
}
}

SameModuloVars { tcx }.relate(a, b).is_ok()
// If it errors for some reason, keep the expected type, not the formal one, keeping the fudging.
let res = OnlyKeepStructuralChanges { infcx }.relate(formal, expected).unwrap_or(expected);
tracing::debug!(
"formal: {formal:?} and expected {expected:?} => only keeping structural changes creates {res:?}"
);
res
}
2 changes: 1 addition & 1 deletion src/llvm-project
Submodule llvm-project updated 114 files
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//@ check-pass
// When we changed from lazy resolving to eager resolving, this was in some cases breaking
// We introduced a workaround in https://github.com/rust-lang/rust/pull/158447 to more often
// avoid breakage. This workaround was not perfect, and we knew this when landing,
// we just had no examples that showed the breakage until this one.
//
// The workaround would try to fudge a formally expected type, but if no structural changes
// to the type happened, would rollback the fudging again, and stuck to the formal expected type.
//
// This rollback behavior worked per function argument, and that's what goes wrong in this
// reproducer: g() is called with two parameters. The 2nd parameter gets fudged, and has a
// structural change (the return type is inferred to unit), so fudging gets accepted,
// not rolled back. In the process, the input of the closure, `x` becomes an unconstrained
// type variable, and type inference fails, despite us knowing that its type is `X` already,
// by the difinion of `g`.

fn f<T>() -> X {
let v = h(g(X, |x| x.m()));
// ^^^^^^^^^^^^^^ two parameters
v
}

fn g<T, U>(_: T, _: fn(T) -> U) -> Y<T, U> {
loop {}
}

fn h<U>(_: Y<U, ()>) -> U {
loop {}
}

struct X;

impl X {
fn m(self) {}
}

struct Y<T, U> {
a: fn(T) -> T,
b: U,
}

fn main() {}
33 changes: 16 additions & 17 deletions tests/ui/const-generics/const-argument-cross-crate-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
error[E0308]: mismatched types
--> $DIR/const-argument-cross-crate-mismatch.rs:6:67
--> $DIR/const-argument-cross-crate-mismatch.rs:6:41
|
LL | let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8]));
| ------------------------- ^^^^^^^^^^ expected an array with a size of 3, found one with a size of 2
| |
| arguments to this struct are incorrect
| --------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3`, found `2`
| |
| arguments to this function are incorrect
|
note: tuple struct defined here
--> $DIR/auxiliary/const_generic_lib.rs:1:12
= note: expected struct `Struct<3>`
found struct `Struct<2>`
note: function defined here
--> $DIR/auxiliary/const_generic_lib.rs:5:8
|
LL | pub struct Struct<const N: usize>(pub [u8; N]);
| ^^^^^^
LL | pub fn function(value: Struct<3>) -> u8 {
| ^^^^^^^^

error[E0308]: mismatched types
--> $DIR/const-argument-cross-crate-mismatch.rs:8:65
--> $DIR/const-argument-cross-crate-mismatch.rs:8:39
|
LL | let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]);
| ------------------------- ^^^^^^^^^^^^^^^ expected an array with a size of 2, found one with a size of 3
| |
| arguments to this struct are incorrect
| ------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2`, found `3`
| |
| expected due to this
|
note: tuple struct defined here
--> $DIR/auxiliary/const_generic_lib.rs:1:12
|
LL | pub struct Struct<const N: usize>(pub [u8; N]);
| ^^^^^^
= note: expected struct `Struct<2>`
found struct `Struct<3>`

error: aborting due to 2 previous errors

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
error: unconstrained generic constant
--> $DIR/issue-62504.rs:18:25
|
LL | ArrayHolder([0; Self::SIZE])
| ^^^^^^^^^^
|
help: try adding a `where` bound
|
LL | pub const fn new() -> Self where [(); Self::SIZE]: {
| +++++++++++++++++++++++

error: unconstrained generic constant
--> $DIR/issue-62504.rs:18:9
|
LL | ArrayHolder([0; Self::SIZE])
| ^^^^^^^^^^^
|
help: try adding a `where` bound
|
LL | pub const fn new() -> Self where [(); Self::SIZE]: {
| +++++++++++++++++++++++

error[E0308]: mismatched types
--> $DIR/issue-62504.rs:18:21
--> $DIR/issue-62504.rs:18:9
|
LL | ArrayHolder([0; Self::SIZE])
| ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`
|
= note: expected constant `X`
found constant `Self::SIZE`

error: unconstrained generic constant
--> $DIR/issue-62504.rs:18:25
--> $DIR/issue-62504.rs:18:9
|
LL | ArrayHolder([0; Self::SIZE])
| ^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try adding a `where` bound
|
LL | pub const fn new() -> Self where [(); Self::SIZE]: {
| +++++++++++++++++++++++

error[E0284]: type annotations needed for `ArrayHolder<_>`
--> $DIR/issue-62504.rs:26:9
--> $DIR/issue-62504.rs:30:9
|
LL | let mut array = ArrayHolder::new();
| ^^^^^^^^^ ----------- type must be known at this point
Expand All @@ -35,7 +57,7 @@ LL | let mut array: ArrayHolder<X> = ArrayHolder::new();
| ++++++++++++++++

error[E0284]: type annotations needed for `ArrayHolder<_>`
--> $DIR/issue-62504.rs:26:9
--> $DIR/issue-62504.rs:30:9
|
LL | let mut array = ArrayHolder::new();
| ^^^^^^^^^ ------------------ type must be known at this point
Expand All @@ -52,7 +74,7 @@ help: consider giving `array` an explicit type, where the value of const paramet
LL | let mut array: ArrayHolder<X> = ArrayHolder::new();
| ++++++++++++++++

error: aborting due to 4 previous errors
error: aborting due to 6 previous errors

Some errors have detailed explanations: E0284, E0308.
For more information about an error, try `rustc --explain E0284`.
Loading
Loading