Skip to content

Commit ea07b96

Browse files
authored
Rollup merge of #98429 - b-naber:use-correct-substs-discriminant-cast, r=lcnr
Use correct substs in enum discriminant cast Fixes #97634 r? ```@lcnr```
2 parents 95ba108 + bf48b62 commit ea07b96

File tree

2 files changed

+104
-92
lines changed

2 files changed

+104
-92
lines changed

compiler/rustc_mir_build/src/thir/cx/expr.rs

+94-92
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::thir::cx::region::Scope;
12
use crate::thir::cx::Cx;
23
use crate::thir::util::UserAnnotatedTyHelpers;
34
use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -158,6 +159,98 @@ impl<'tcx> Cx<'tcx> {
158159
Expr { temp_lifetime, ty: adjustment.target, span, kind }
159160
}
160161

162+
/// Lowers a cast expression.
163+
///
164+
/// Dealing with user type annotations is left to the caller.
165+
fn mirror_expr_cast(
166+
&mut self,
167+
source: &'tcx hir::Expr<'tcx>,
168+
temp_lifetime: Option<Scope>,
169+
span: Span,
170+
) -> ExprKind<'tcx> {
171+
let tcx = self.tcx;
172+
173+
// Check to see if this cast is a "coercion cast", where the cast is actually done
174+
// using a coercion (or is a no-op).
175+
if self.typeck_results().is_coercion_cast(source.hir_id) {
176+
// Convert the lexpr to a vexpr.
177+
ExprKind::Use { source: self.mirror_expr(source) }
178+
} else if self.typeck_results().expr_ty(source).is_region_ptr() {
179+
// Special cased so that we can type check that the element
180+
// type of the source matches the pointed to type of the
181+
// destination.
182+
ExprKind::Pointer {
183+
source: self.mirror_expr(source),
184+
cast: PointerCast::ArrayToPointer,
185+
}
186+
} else {
187+
// check whether this is casting an enum variant discriminant
188+
// to prevent cycles, we refer to the discriminant initializer
189+
// which is always an integer and thus doesn't need to know the
190+
// enum's layout (or its tag type) to compute it during const eval
191+
// Example:
192+
// enum Foo {
193+
// A,
194+
// B = A as isize + 4,
195+
// }
196+
// The correct solution would be to add symbolic computations to miri,
197+
// so we wouldn't have to compute and store the actual value
198+
199+
let hir::ExprKind::Path(ref qpath) = source.kind else {
200+
return ExprKind::Cast { source: self.mirror_expr(source)};
201+
};
202+
203+
let res = self.typeck_results().qpath_res(qpath, source.hir_id);
204+
let ty = self.typeck_results().node_type(source.hir_id);
205+
let ty::Adt(adt_def, substs) = ty.kind() else {
206+
return ExprKind::Cast { source: self.mirror_expr(source)};
207+
};
208+
209+
let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res else {
210+
return ExprKind::Cast { source: self.mirror_expr(source)};
211+
};
212+
213+
let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id);
214+
let (discr_did, discr_offset) = adt_def.discriminant_def_for_variant(idx);
215+
216+
use rustc_middle::ty::util::IntTypeExt;
217+
let ty = adt_def.repr().discr_type();
218+
let discr_ty = ty.to_ty(tcx);
219+
220+
let param_env_ty = self.param_env.and(discr_ty);
221+
let size = tcx
222+
.layout_of(param_env_ty)
223+
.unwrap_or_else(|e| {
224+
panic!("could not compute layout for {:?}: {:?}", param_env_ty, e)
225+
})
226+
.size;
227+
228+
let lit = ScalarInt::try_from_uint(discr_offset as u128, size).unwrap();
229+
let kind = ExprKind::NonHirLiteral { lit, user_ty: None };
230+
let offset = self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind });
231+
232+
let source = match discr_did {
233+
// in case we are offsetting from a computed discriminant
234+
// and not the beginning of discriminants (which is always `0`)
235+
Some(did) => {
236+
let kind = ExprKind::NamedConst { def_id: did, substs, user_ty: None };
237+
let lhs =
238+
self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind });
239+
let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset };
240+
self.thir.exprs.push(Expr {
241+
temp_lifetime,
242+
ty: discr_ty,
243+
span: span,
244+
kind: bin,
245+
})
246+
}
247+
None => offset,
248+
};
249+
250+
ExprKind::Cast { source }
251+
}
252+
}
253+
161254
fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
162255
let tcx = self.tcx;
163256
let expr_ty = self.typeck_results().expr_ty(expr);
@@ -604,98 +697,7 @@ impl<'tcx> Cx<'tcx> {
604697
expr, cast_ty.hir_id, user_ty,
605698
);
606699

607-
// Check to see if this cast is a "coercion cast", where the cast is actually done
608-
// using a coercion (or is a no-op).
609-
let cast = if self.typeck_results().is_coercion_cast(source.hir_id) {
610-
// Convert the lexpr to a vexpr.
611-
ExprKind::Use { source: self.mirror_expr(source) }
612-
} else if self.typeck_results().expr_ty(source).is_region_ptr() {
613-
// Special cased so that we can type check that the element
614-
// type of the source matches the pointed to type of the
615-
// destination.
616-
ExprKind::Pointer {
617-
source: self.mirror_expr(source),
618-
cast: PointerCast::ArrayToPointer,
619-
}
620-
} else {
621-
// check whether this is casting an enum variant discriminant
622-
// to prevent cycles, we refer to the discriminant initializer
623-
// which is always an integer and thus doesn't need to know the
624-
// enum's layout (or its tag type) to compute it during const eval
625-
// Example:
626-
// enum Foo {
627-
// A,
628-
// B = A as isize + 4,
629-
// }
630-
// The correct solution would be to add symbolic computations to miri,
631-
// so we wouldn't have to compute and store the actual value
632-
let var = if let hir::ExprKind::Path(ref qpath) = source.kind {
633-
let res = self.typeck_results().qpath_res(qpath, source.hir_id);
634-
self.typeck_results().node_type(source.hir_id).ty_adt_def().and_then(
635-
|adt_def| match res {
636-
Res::Def(
637-
DefKind::Ctor(CtorOf::Variant, CtorKind::Const),
638-
variant_ctor_id,
639-
) => {
640-
let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id);
641-
let (d, o) = adt_def.discriminant_def_for_variant(idx);
642-
use rustc_middle::ty::util::IntTypeExt;
643-
let ty = adt_def.repr().discr_type();
644-
let ty = ty.to_ty(tcx);
645-
Some((d, o, ty))
646-
}
647-
_ => None,
648-
},
649-
)
650-
} else {
651-
None
652-
};
653-
654-
let source = if let Some((did, offset, var_ty)) = var {
655-
let param_env_ty = self.param_env.and(var_ty);
656-
let size = tcx
657-
.layout_of(param_env_ty)
658-
.unwrap_or_else(|e| {
659-
panic!("could not compute layout for {:?}: {:?}", param_env_ty, e)
660-
})
661-
.size;
662-
let lit = ScalarInt::try_from_uint(offset as u128, size).unwrap();
663-
let kind = ExprKind::NonHirLiteral { lit, user_ty: None };
664-
let offset = self.thir.exprs.push(Expr {
665-
temp_lifetime,
666-
ty: var_ty,
667-
span: expr.span,
668-
kind,
669-
});
670-
match did {
671-
Some(did) => {
672-
// in case we are offsetting from a computed discriminant
673-
// and not the beginning of discriminants (which is always `0`)
674-
let substs = InternalSubsts::identity_for_item(tcx, did);
675-
let kind =
676-
ExprKind::NamedConst { def_id: did, substs, user_ty: None };
677-
let lhs = self.thir.exprs.push(Expr {
678-
temp_lifetime,
679-
ty: var_ty,
680-
span: expr.span,
681-
kind,
682-
});
683-
let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset };
684-
self.thir.exprs.push(Expr {
685-
temp_lifetime,
686-
ty: var_ty,
687-
span: expr.span,
688-
kind: bin,
689-
})
690-
}
691-
None => offset,
692-
}
693-
} else {
694-
self.mirror_expr(source)
695-
};
696-
697-
ExprKind::Cast { source: source }
698-
};
700+
let cast = self.mirror_expr_cast(*source, temp_lifetime, expr.span);
699701

700702
if let Some(user_ty) = user_ty {
701703
// NOTE: Creating a new Expr and wrapping a Cast inside of it may be
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// build-pass
2+
3+
pub enum Register<const N: u16> {
4+
Field0 = 40,
5+
Field1,
6+
}
7+
8+
fn main() {
9+
let _b = Register::<0>::Field1 as u16;
10+
}

0 commit comments

Comments
 (0)