Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f5a2bb6
Add hygiene annotations for tokens in macro_rules! bodies
aytey Mar 2, 2026
bf6db4f
Add regression tests for token hygiene annotations in macro bodies
aytey Mar 3, 2026
7ec77fd
Fix invalid suggestion on `for-loops-over-fallibles`
JohnTitor Mar 15, 2026
924374c
mGCA: Lower const generic args to infer when needed
reddevilmidzy Mar 17, 2026
11810b8
fix inference variables leaking into HIR const lowering logic
Human9000-bit Mar 8, 2026
5113488
Improve `ty::Infer` handling during const literal lowering
Human9000-bit Mar 17, 2026
b4c1677
Do not lint `for-loops-over-fallibles` on external macros
JohnTitor Mar 15, 2026
cfe18cf
Correct dir name
JohnTitor Mar 15, 2026
38aa322
borrowck/type_check: remove helper left-over from unsized locals
RalfJung Mar 18, 2026
6b0fecf
merge `regions-outlives-nominal-type-*` tests into one file
cyrgani Mar 18, 2026
c8a2c46
bootstrap: Optionally print a backtrace if a command fails
jyn514 Mar 16, 2026
a085b68
Add bootstrap step for stdarch-verify
xonx4l Mar 18, 2026
49bb371
When single impl can satisfy inference error, suggest type
estebank Mar 11, 2026
34f8a6c
Rollup merge of #153957 - xonx4l:stdarch-verify, r=Kobzol
JonathanBrouwer Mar 18, 2026
7c5863e
Rollup merge of #153727 - estebank:issue-100802, r=JohnTitor
JonathanBrouwer Mar 18, 2026
7e0653c
Rollup merge of #153308 - aytey:macro_meta_hygiene, r=jdonszelmann
JonathanBrouwer Mar 18, 2026
54ff72b
Rollup merge of #153557 - Human9000-bit:issue-153525, r=BoxyUwU
JonathanBrouwer Mar 18, 2026
a65f8fe
Rollup merge of #153913 - JohnTitor:issue-148814, r=Kivooeo
JonathanBrouwer Mar 18, 2026
44f76c3
Rollup merge of #153987 - reddevilmidzy:mgca-ast, r=BoxyUwU
JonathanBrouwer Mar 18, 2026
99112bf
Rollup merge of #153992 - ferrocene:jyn/bootstrap-backtrace, r=jieyou…
JonathanBrouwer Mar 18, 2026
8af84bf
Rollup merge of #154036 - RalfJung:unsized-locals-helper-rm, r=nnethe…
JonathanBrouwer Mar 18, 2026
3f102c1
Rollup merge of #154038 - cyrgani:regiontests, r=Kivooeo
JonathanBrouwer Mar 18, 2026
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
10 changes: 7 additions & 3 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1310,9 +1310,13 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
}
GenericArg::Type(self.lower_ty_alloc(ty, itctx).try_as_ambig_ty().unwrap())
}
ast::GenericArg::Const(ct) => GenericArg::Const(
self.lower_anon_const_to_const_arg_and_alloc(ct).try_as_ambig_ct().unwrap(),
),
ast::GenericArg::Const(ct) => {
let ct = self.lower_anon_const_to_const_arg_and_alloc(ct);
match ct.try_as_ambig_ct() {
Some(ct) => GenericArg::Const(ct),
None => GenericArg::Infer(hir::InferArg { hir_id: ct.hir_id, span: ct.span }),
}
}
}
}

Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,23 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
TokenTree::Token(token, spacing) => {
let token_str = self.token_to_string_ext(token, convert_dollar_crate);
self.word(token_str);
// Emit hygiene annotations for identity-bearing tokens,
// matching how print_ident() and print_lifetime() call ann_post().
match token.kind {
token::Ident(name, _) => {
self.ann_post(Ident::new(name, token.span));
}
token::NtIdent(ident, _) => {
self.ann_post(ident);
}
token::Lifetime(name, _) => {
self.ann_post(Ident::new(name, token.span));
}
token::NtLifetime(ident, _) => {
self.ann_post(ident);
}
_ => {}
}
if let token::DocComment(..) = token.kind {
self.hardbreak()
}
Expand Down
17 changes: 7 additions & 10 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,10 +374,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.body
}

fn unsized_feature_enabled(&self) -> bool {
self.tcx().features().unsized_fn_params()
}

/// Equate the inferred type and the annotated type for user type annotations
#[instrument(skip(self), level = "debug")]
fn check_user_type_annotations(&mut self) {
Expand Down Expand Up @@ -660,7 +656,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}

if !self.unsized_feature_enabled() {
if !self.tcx().features().unsized_fn_params() {
let trait_ref = ty::TraitRef::new(
tcx,
tcx.require_lang_item(LangItem::Sized, self.last_span),
Expand Down Expand Up @@ -936,9 +932,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}

// When `unsized_fn_params` is enabled, only function calls
// and nullary ops are checked in `check_call_dest`.
if !self.unsized_feature_enabled() {
// When `unsized_fn_params` is enabled, this is checked in `check_call_dest`,
// and `hir_typeck` still forces all non-argument locals to be sized (i.e., we don't
// fully re-check what was already checked on HIR).
if !self.tcx().features().unsized_fn_params() {
match self.body.local_kind(local) {
LocalKind::ReturnPointer | LocalKind::Arg => {
// return values of normal functions are required to be
Expand Down Expand Up @@ -1953,8 +1950,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}

// When `unsized_fn_params` is not enabled,
// this check is done at `check_local`.
if self.unsized_feature_enabled() {
// this check is done at `visit_local_decl`.
if self.tcx().features().unsized_fn_params() {
let span = term.source_info.span;
self.ensure_place_sized(dest_ty, span);
}
Expand Down
13 changes: 10 additions & 3 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2874,6 +2874,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span: Span,
) -> Const<'tcx> {
let tcx = self.tcx();

let ty = if !ty.has_infer() { Some(ty) } else { None };

if let LitKind::Err(guar) = *kind {
return ty::Const::new_error(tcx, guar);
}
Expand Down Expand Up @@ -2905,16 +2908,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
};

let lit_input = match expr.kind {
hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: false }),
hir::ExprKind::Lit(lit) => {
Some(LitToConstInput { lit: lit.node, ty: Some(ty), neg: false })
}
hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind {
hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: true }),
hir::ExprKind::Lit(lit) => {
Some(LitToConstInput { lit: lit.node, ty: Some(ty), neg: true })
}
_ => None,
},
_ => None,
};

lit_input.and_then(|l| {
if const_lit_matches_ty(tcx, &l.lit, l.ty, l.neg) {
if const_lit_matches_ty(tcx, &l.lit, ty, l.neg) {
tcx.at(expr.span)
.lit_to_const(l)
.map(|value| ty::Const::new_value(tcx, value.valtree, value.ty))
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_lint/src/for_loops_over_fallibles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some((pat, arg)) = extract_for_loop(expr) else { return };

// Do not put suggestions for external macros.
if pat.span.from_expansion() {
return;
}

let arg_span = arg.span.source_callsite();

let ty = cx.typeck_results().expr_ty(arg);
Expand Down Expand Up @@ -77,6 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
};

let sub = if let Some(recv) = extract_iterator_next_call(cx, arg)
&& recv.span.can_be_used_for_suggestions()
&& recv.span.between(arg_span.shrink_to_hi()).can_be_used_for_suggestions()
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
{
ForLoopsOverFalliblesLoopSub::RemoveNext {
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_middle/src/ty/consts/lit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ pub struct LitToConstInput<'tcx> {
/// The absolute value of the resultant constant.
pub lit: LitKind,
/// The type of the constant.
pub ty: Ty<'tcx>,
///
/// `None` is used by const generics when the type of the constant is unknown, e.g.
/// if there are inference variables
pub ty: Option<Ty<'tcx>>,
/// If the constant is negative.
pub neg: bool,
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_mir_build/src/builder/expr/as_constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ pub(crate) fn as_constant_inner<'tcx>(

match *kind {
ExprKind::Literal { lit, neg } => {
let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty, neg });
let const_ =
lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty: Some(ty), neg });

ConstOperand { span, user_ty: None, const_ }
}
Expand Down Expand Up @@ -109,6 +110,8 @@ pub(crate) fn as_constant_inner<'tcx>(
fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>) -> Const<'tcx> {
let LitToConstInput { lit, ty, neg } = lit_input;

let ty = ty.expect("type of literal must be known at this point");

if let Err(guar) = ty.error_reported() {
return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar));
}
Expand Down
22 changes: 12 additions & 10 deletions compiler/rustc_mir_build/src/thir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,27 @@ pub(crate) fn lit_to_const<'tcx>(
.unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result))
};

let (valtree, valtree_ty) = match (lit, expected_ty.kind()) {
let (valtree, valtree_ty) = match (lit, expected_ty.map(|ty| ty.kind())) {
(ast::LitKind::Str(s, _), _) => {
let str_bytes = s.as_str().as_bytes();
let valtree_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_);
(ty::ValTree::from_raw_bytes(tcx, str_bytes), valtree_ty)
}
(ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _))
(ast::LitKind::ByteStr(byte_sym, _), Some(ty::Ref(_, inner_ty, _)))
if let ty::Slice(ty) | ty::Array(ty, _) = inner_ty.kind()
&& let ty::Uint(UintTy::U8) = ty.kind() =>
{
(ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty)
(ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap())
}
(ast::LitKind::ByteStr(byte_sym, _), ty::Slice(inner_ty) | ty::Array(inner_ty, _))
if tcx.features().deref_patterns()
&& let ty::Uint(UintTy::U8) = inner_ty.kind() =>
(
ast::LitKind::ByteStr(byte_sym, _),
Some(ty::Slice(inner_ty) | ty::Array(inner_ty, _)),
) if tcx.features().deref_patterns()
&& let ty::Uint(UintTy::U8) = inner_ty.kind() =>
{
// Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is
// enabled, in order to allow, e.g., `deref!(b"..."): Vec<u8>`.
(ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty)
(ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap())
}
(ast::LitKind::ByteStr(byte_sym, _), _) => {
let valtree = ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str());
Expand Down Expand Up @@ -79,11 +81,11 @@ pub(crate) fn lit_to_const<'tcx>(
trunc(if neg { u128::wrapping_neg(n.get()) } else { n.get() }, i.to_unsigned());
(ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_int(tcx, i))
}
(ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), ty::Uint(ui)) if !neg => {
(ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Uint(ui))) if !neg => {
let scalar_int = trunc(n.get(), *ui);
(ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_uint(tcx, *ui))
}
(ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), ty::Int(i)) => {
(ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Int(i))) => {
// Unsigned "negation" has the same bitwise effect as signed negation,
// which gets the result we want without additional casts.
let scalar_int =
Expand All @@ -101,7 +103,7 @@ pub(crate) fn lit_to_const<'tcx>(
let bits = parse_float_into_scalar(n, fty, neg)?;
(ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, fty))
}
(ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), ty::Float(fty)) => {
(ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), Some(ty::Float(fty))) => {
let bits = parse_float_into_scalar(n, *fty, neg)?;
(ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, *fty))
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/thir/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ impl<'tcx> PatCtxt<'tcx> {
// patterns to `str`, and byte-string literal patterns to `[u8; N]` or `[u8]`.

let pat_ty = self.typeck_results.node_type(pat.hir_id);
let lit_input = LitToConstInput { lit: lit.node, ty: pat_ty, neg: *negated };
let lit_input = LitToConstInput { lit: lit.node, ty: Some(pat_ty), neg: *negated };
let constant = const_lit_matches_ty(self.tcx, &lit.node, pat_ty, *negated)
.then(|| self.tcx.at(expr.span).lit_to_const(lit_input))
.flatten()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ impl UnderspecifiedArgKind {

struct ClosureEraser<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
depth: usize,
}

impl<'a, 'tcx> ClosureEraser<'a, 'tcx> {
Expand All @@ -172,7 +173,8 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'a, 'tcx> {
}

fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
match ty.kind() {
self.depth += 1;
let ty = match ty.kind() {
ty::Closure(_, args) => {
// For a closure type, we turn it into a function pointer so that it gets rendered
// as `fn(args) -> Ret`.
Expand Down Expand Up @@ -233,9 +235,15 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'a, 'tcx> {
// its type parameters.
ty.super_fold_with(self)
}
// We don't have an unknown type parameter anywhere, replace with `_`.
// We are in the top-level type, not one of its type parameters. Name it with its
// parameters replaced.
_ if self.depth == 1 => ty.super_fold_with(self),
// We don't have an unknown type parameter anywhere, and we are in a type parameter.
// Replace with `_`.
_ => self.new_infer(),
}
};
self.depth -= 1;
ty
}

fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
Expand Down Expand Up @@ -287,7 +295,7 @@ fn ty_to_string<'tcx>(
let ty = infcx.resolve_vars_if_possible(ty);
// We use `fn` ptr syntax for closures, but this only works when the closure does not capture
// anything. We also remove all type parameters that are fully known to the type system.
let ty = ty.fold_with(&mut ClosureEraser { infcx });
let ty = ty.fold_with(&mut ClosureEraser { infcx, depth: 0 });

match (ty.kind(), called_method_def_id) {
// We don't want the regular output for `fn`s because it includes its path in
Expand Down Expand Up @@ -467,6 +475,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
term: Term<'tcx>,
error_code: TypeAnnotationNeeded,
should_label_span: bool,
) -> Diag<'a> {
self.emit_inference_failure_err_with_type_hint(
body_def_id,
failure_span,
term,
error_code,
should_label_span,
None,
)
}

pub fn emit_inference_failure_err_with_type_hint(
&self,
body_def_id: LocalDefId,
failure_span: Span,
term: Term<'tcx>,
error_code: TypeAnnotationNeeded,
should_label_span: bool,
ty: Option<Ty<'tcx>>,
) -> Diag<'a> {
let term = self.resolve_vars_if_possible(term);
let arg_data = self
Expand All @@ -479,7 +506,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
return self.bad_inference_failure_err(failure_span, arg_data, error_code);
};

let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term);
let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term, ty);
if let Some(body) = self.tcx.hir_maybe_body_owned_by(
self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(),
) {
Expand Down Expand Up @@ -779,10 +806,20 @@ impl<'tcx> InferSourceKind<'tcx> {
| InferSourceKind::ClosureReturn { ty, .. } => {
if ty.is_closure() {
("closure", closure_as_fn_str(infcx, ty), long_ty_path)
} else if !ty.is_ty_or_numeric_infer() {
("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path)
} else {
} else if ty.is_ty_or_numeric_infer()
|| ty.is_primitive()
|| matches!(
ty.kind(),
ty::Adt(_, args)
if args.types().count() == 0 && args.consts().count() == 0
)
{
// `ty` is either `_`, a primitive type like `u32` or a type with no type or
// const parameters. We will not mention the type in the main inference error
// message.
("other", String::new(), long_ty_path)
} else {
("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path)
}
}
// FIXME: We should be able to add some additional info here.
Expand Down Expand Up @@ -815,6 +852,7 @@ struct FindInferSourceVisitor<'a, 'tcx> {
typeck_results: &'a TypeckResults<'tcx>,

target: Term<'tcx>,
ty: Option<Ty<'tcx>>,

attempt: usize,
infer_source_cost: usize,
Expand All @@ -826,12 +864,14 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
tecx: &'a TypeErrCtxt<'a, 'tcx>,
typeck_results: &'a TypeckResults<'tcx>,
target: Term<'tcx>,
ty: Option<Ty<'tcx>>,
) -> Self {
FindInferSourceVisitor {
tecx,
typeck_results,

target,
ty,

attempt: 0,
infer_source_cost: usize::MAX,
Expand Down Expand Up @@ -1213,7 +1253,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) {
intravisit::walk_local(self, local);

if let Some(ty) = self.opt_node_type(local.hir_id) {
if let Some(mut ty) = self.opt_node_type(local.hir_id) {
if self.generic_arg_contains_target(ty.into()) {
fn get_did(
typeck_results: &TypeckResults<'_>,
Expand Down Expand Up @@ -1241,7 +1281,11 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
_ => None,
}
}

if let Some(t) = self.ty
&& ty.has_infer()
{
ty = t;
}
if let LocalSource::Normal = local.source
&& local.ty.is_none()
{
Expand Down
Loading
Loading