Skip to content

Commit e5f35df

Browse files
committed
Auto merge of rust-lang#70825 - eddyb:enum-discr-correct-generics-parent, r=nikomatsakis
typeck: always expose explicit enum discriminant `AnonConst`s' parent in `generics_of`. This is similar to rust-lang#70452 but for explicit `enum` discriminant constant expressions. However, unlike rust-lang#70452, this PR should have no effect on stable code, as while it alleviates rust-lang#43408 errors, there is no way to actually compile an `enum` with generic parameters *and* explicit discriminants, without `#![feature(arbitrary_enum_discriminant)]`, as explicit discriminant expression don't count as uses of parameters (if they did, they would count as invariant uses). <hr/> There's also 2 other commits here, both related to rust-lang#70453: * "ty: use `delay_span_bug` in `ty::AdtDef::eval_explicit_discr`." - hides the ICEs demonstrated on rust-lang#70453, when there are other errors (which the next commit adds) * "typeck/wfcheck: require that explicit enum discriminants const-evaluate succesfully." - closes rust-lang#70453 by picking alternative "2", i.e. erroring when a discriminant doesn't fully const-evaluate from the perspective of the `enum` definition In the future, it might be possible to allow `enum` discriminants to actually depend on parameters, but that will likely require rust-lang#68436 + some way to restrict the values so no two variants can end up with overlapping discriminants. As this PR would close rust-lang#70453, it shouldn't be merged until a decision is reached there. r? @nikomatsakis
2 parents d626e4d + 926c7a2 commit e5f35df

8 files changed

+126
-19
lines changed

src/librustc_middle/ty/mod.rs

+8-15
Original file line numberDiff line numberDiff line change
@@ -2388,21 +2388,14 @@ impl<'tcx> AdtDef {
23882388
None
23892389
}
23902390
}
2391-
Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted) => {
2392-
if !expr_did.is_local() {
2393-
span_bug!(
2394-
tcx.def_span(expr_did),
2395-
"variant discriminant evaluation succeeded \
2396-
in its crate but failed locally"
2397-
);
2398-
}
2399-
None
2400-
}
2401-
Err(ErrorHandled::TooGeneric) => {
2402-
tcx.sess.delay_span_bug(
2403-
tcx.def_span(expr_did),
2404-
"enum discriminant depends on generic arguments",
2405-
);
2391+
Err(err) => {
2392+
let msg = match err {
2393+
ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
2394+
"enum discriminant evaluation failed"
2395+
}
2396+
ErrorHandled::TooGeneric => "enum discriminant depends on generics",
2397+
};
2398+
tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg);
24062399
None
24072400
}
24082401
}

src/librustc_typeck/check/wfcheck.rs

+35-2
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,23 @@ fn check_type_defn<'tcx, F>(
411411
ObligationCauseCode::MiscObligation,
412412
)
413413
}
414+
415+
// Explicit `enum` discriminant values must const-evaluate successfully.
416+
if let Some(discr_def_id) = variant.explicit_discr {
417+
let discr_substs =
418+
InternalSubsts::identity_for_item(fcx.tcx, discr_def_id.to_def_id());
419+
420+
let cause = traits::ObligationCause::new(
421+
fcx.tcx.def_span(discr_def_id),
422+
fcx.body_id,
423+
traits::MiscObligation,
424+
);
425+
fcx.register_predicate(traits::Obligation::new(
426+
cause,
427+
fcx.param_env,
428+
ty::Predicate::ConstEvaluatable(discr_def_id.to_def_id(), discr_substs),
429+
));
430+
}
414431
}
415432

416433
check_where_clauses(tcx, fcx, item.span, def_id.to_def_id(), None);
@@ -1287,8 +1304,14 @@ impl ParItemLikeVisitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> {
12871304
///////////////////////////////////////////////////////////////////////////
12881305
// ADT
12891306

1307+
// FIXME(eddyb) replace this with getting fields/discriminants through `ty::AdtDef`.
12901308
struct AdtVariant<'tcx> {
1309+
/// Types of fields in the variant, that must be well-formed.
12911310
fields: Vec<AdtField<'tcx>>,
1311+
1312+
/// Explicit discriminant of this variant (e.g. `A = 123`),
1313+
/// that must evaluate to a constant value.
1314+
explicit_discr: Option<LocalDefId>,
12921315
}
12931316

12941317
struct AdtField<'tcx> {
@@ -1297,6 +1320,7 @@ struct AdtField<'tcx> {
12971320
}
12981321

12991322
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1323+
// FIXME(eddyb) replace this with getting fields through `ty::AdtDef`.
13001324
fn non_enum_variant(&self, struct_def: &hir::VariantData<'_>) -> AdtVariant<'tcx> {
13011325
let fields = struct_def
13021326
.fields()
@@ -1309,11 +1333,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13091333
AdtField { ty: field_ty, span: field.span }
13101334
})
13111335
.collect();
1312-
AdtVariant { fields }
1336+
AdtVariant { fields, explicit_discr: None }
13131337
}
13141338

13151339
fn enum_variants(&self, enum_def: &hir::EnumDef<'_>) -> Vec<AdtVariant<'tcx>> {
1316-
enum_def.variants.iter().map(|variant| self.non_enum_variant(&variant.data)).collect()
1340+
enum_def
1341+
.variants
1342+
.iter()
1343+
.map(|variant| AdtVariant {
1344+
fields: self.non_enum_variant(&variant.data).fields,
1345+
explicit_discr: variant
1346+
.disr_expr
1347+
.map(|explicit_discr| self.tcx.hir().local_def_id(explicit_discr.hir_id)),
1348+
})
1349+
.collect()
13171350
}
13181351

13191352
fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> {

src/librustc_typeck/collect.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1178,9 +1178,11 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
11781178
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
11791179
match parent_node {
11801180
// HACK(eddyb) this provides the correct generics for repeat
1181-
// expressions' count (i.e. `N` in `[x; N]`), as they shouldn't
1182-
// be able to cause query cycle errors.
1181+
// expressions' count (i.e. `N` in `[x; N]`), and explicit
1182+
// `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`),
1183+
// as they shouldn't be able to cause query cycle errors.
11831184
Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
1185+
| Node::Variant(Variant { disr_expr: Some(ref constant), .. })
11841186
if constant.hir_id == hir_id =>
11851187
{
11861188
Some(parent_def_id.to_def_id())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![feature(arbitrary_enum_discriminant, core_intrinsics)]
2+
3+
extern crate core;
4+
use core::intrinsics::discriminant_value;
5+
6+
#[repr(usize)]
7+
enum MyWeirdOption<T> {
8+
None = 0,
9+
Some(T) = std::mem::size_of::<T>(),
10+
//~^ ERROR constant expression depends on a generic parameter
11+
}
12+
13+
fn main() {
14+
assert_eq!(discriminant_value(&MyWeirdOption::<u8>::None), 0);
15+
assert_eq!(discriminant_value(&MyWeirdOption::Some(0u8)), 1);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: constant expression depends on a generic parameter
2+
--> $DIR/issue-70453-generics-in-discr-ice-2.rs:9:15
3+
|
4+
LL | Some(T) = std::mem::size_of::<T>(),
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: this may fail depending on what value the parameter takes
8+
9+
error: aborting due to previous error
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(core_intrinsics)]
2+
3+
extern crate core;
4+
use core::intrinsics::discriminant_value;
5+
6+
#[repr(usize)]
7+
enum MyWeirdOption<T> {
8+
//~^ ERROR parameter `T` is never used
9+
None = 0,
10+
Some = std::mem::size_of::<T>(),
11+
//~^ ERROR constant expression depends on a generic parameter
12+
}
13+
14+
fn main() {
15+
assert_eq!(discriminant_value(&MyWeirdOption::<u8>::None), 0);
16+
assert_eq!(discriminant_value(&MyWeirdOption::<u8>::Some), 1);
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: constant expression depends on a generic parameter
2+
--> $DIR/issue-70453-generics-in-discr-ice.rs:10:12
3+
|
4+
LL | Some = std::mem::size_of::<T>(),
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: this may fail depending on what value the parameter takes
8+
9+
error[E0392]: parameter `T` is never used
10+
--> $DIR/issue-70453-generics-in-discr-ice.rs:7:20
11+
|
12+
LL | enum MyWeirdOption<T> {
13+
| ^ unused parameter
14+
|
15+
= help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData`
16+
17+
error: aborting due to 2 previous errors
18+
19+
For more information about this error, try `rustc --explain E0392`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-pass
2+
3+
#![feature(arbitrary_enum_discriminant, core_intrinsics)]
4+
5+
extern crate core;
6+
use core::intrinsics::discriminant_value;
7+
8+
#[repr(usize)]
9+
enum MyWeirdOption<T> {
10+
None = 0,
11+
Some(T) = core::mem::size_of::<*mut T>(),
12+
}
13+
14+
fn main() {
15+
assert_eq!(discriminant_value(&MyWeirdOption::<()>::None), 0);
16+
assert_eq!(discriminant_value(&MyWeirdOption::Some(())), core::mem::size_of::<usize>() as u64);
17+
}

0 commit comments

Comments
 (0)