From 7bde18a0f3dc52754eb52d09da0bc259b1a0e757 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Tue, 19 Oct 2021 11:08:12 +0800 Subject: [PATCH 01/17] implement type-changing-struct-update put the test dir in test/ui/rfcs --- compiler/rustc_middle/src/ty/error.rs | 3 + .../rustc_middle/src/ty/structural_impls.rs | 1 + compiler/rustc_typeck/src/check/expr.rs | 120 +++++++++++++----- .../type-changing-struct-update.md | 33 +++++ .../feature-gate.rs} | 2 + .../feature-gate.stderr} | 2 +- .../lifetime-update.rs | 43 +++++++ .../lifetime-update.stderr | 15 +++ .../type-generic-update.rs | 57 +++++++++ .../type-generic-update.stderr | 30 +++++ 10 files changed, 276 insertions(+), 30 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/type-changing-struct-update.md rename src/test/ui/{feature-gates/feature-gate-type_changing_struct_update.rs => rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs} (92%) rename src/test/ui/{feature-gates/feature-gate-type_changing_struct_update.stderr => rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr} (84%) create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.rs create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.stderr create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 2bd9415171d7d..b14a69892657b 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -42,6 +42,7 @@ pub enum TypeError<'tcx> { TupleSize(ExpectedFound), FixedArraySize(ExpectedFound), ArgCount, + FieldMisMatch(Symbol, Symbol), RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>), @@ -134,6 +135,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { pluralize!(values.found) ), ArgCount => write!(f, "incorrect number of function parameters"), + FieldMisMatch(adt, field) => write!(f, "field type mismatch: {}.{}", adt, field), RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"), RegionsInsufficientlyPolymorphic(br, _) => write!( f, @@ -224,6 +226,7 @@ impl<'tcx> TypeError<'tcx> { | ArgumentMutability(_) | TupleSize(_) | ArgCount + | FieldMisMatch(..) | RegionsDoesNotOutlive(..) | RegionsInsufficientlyPolymorphic(..) | RegionsOverlyPolymorphic(..) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index d6069395474ab..0f8e80806e31e 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -602,6 +602,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { TupleSize(x) => TupleSize(x), FixedArraySize(x) => FixedArraySize(x), ArgCount => ArgCount, + FieldMisMatch(x, y) => FieldMisMatch(x, y), RegionsDoesNotOutlive(a, b) => { return tcx.lift((a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b)); } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 3846aad2cfc10..25ad6b659a799 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -23,7 +23,7 @@ use crate::type_error_struct; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorReported; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; @@ -33,8 +33,10 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ExprKind, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::InferOk; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; +use rustc_middle::ty::error::TypeError::{FieldMisMatch, Mismatch}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; @@ -1262,7 +1264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); } - let error_happened = self.check_expr_struct_fields( + let (error_happened, mut remaining_fields) = self.check_expr_struct_fields( adt_ty, expected, expr.hir_id, @@ -1277,32 +1279,92 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the fields with the base_expr. This could cause us to hit errors later // when certain fields are assumed to exist that in fact do not. if !error_happened { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {}); - match adt_ty.kind() { - ty::Adt(adt, substs) if adt.is_struct() => { - let fru_field_types = adt - .non_enum_variant() - .fields - .iter() - .map(|f| { - self.normalize_associated_types_in( - expr.span, - f.ty(self.tcx, substs), - ) - }) - .collect(); - - self.typeck_results - .borrow_mut() - .fru_field_types_mut() - .insert(expr.hir_id, fru_field_types); + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + if self.tcx.features().type_changing_struct_update { + let base_ty = self.check_expr(base_expr); + match (adt_ty.kind(), base_ty.kind()) { + (ty::Adt(adt, substs), ty::Adt(base_adt, base_subs)) if adt == base_adt => { + if !adt.is_struct() { + self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct { + span: base_expr.span, + }); + }; + let fru_field_types = variant + .fields + .iter() + .map(|f| { + let fru_ty = self.normalize_associated_types_in( + expr.span, + self.field_ty(base_expr.span, f, base_subs), + ); + let ident = self.tcx.adjust_ident(f.ident, variant.def_id); + if remaining_fields.remove(&ident) { + let target_ty = self.field_ty(base_expr.span, f, substs); + let cause = self.misc(base_expr.span); + match self.at(&cause, self.param_env).sup(target_ty, fru_ty) + { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations) + } + // FIXME: Needs better diagnostics here + Err(_) => self + .report_mismatched_types( + &cause, + target_ty, + fru_ty, + FieldMisMatch(variant.ident.name, ident.name), + ) + .emit(), + } + } + fru_ty + }) + .collect(); + + self.typeck_results + .borrow_mut() + .fru_field_types_mut() + .insert(expr.hir_id, fru_field_types); + } + _ => { + self.report_mismatched_types( + &self.misc(base_expr.span), + adt_ty, + base_ty, + Mismatch, + ) + .emit(); + } } - _ => { - self.tcx - .sess - .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); + } else { + self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {}); + match adt_ty.kind() { + ty::Adt(adt, substs) if adt.is_struct() => { + let fru_field_types = adt + .non_enum_variant() + .fields + .iter() + .map(|f| { + self.normalize_associated_types_in( + expr.span, + f.ty(self.tcx, substs), + ) + }) + .collect(); + + self.typeck_results + .borrow_mut() + .fru_field_types_mut() + .insert(expr.hir_id, fru_field_types); + } + _ => { + self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct { + span: base_expr.span, + }); + } } - } + }; } } self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized); @@ -1319,7 +1381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast_fields: &'tcx [hir::ExprField<'tcx>], check_completeness: bool, expr_span: Span, - ) -> bool { + ) -> (bool, FxHashSet) { let tcx = self.tcx; let adt_ty_hint = self @@ -1402,11 +1464,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if inaccessible_remaining_fields { self.report_inaccessible_fields(adt_ty, span); } else { - self.report_missing_fields(adt_ty, span, remaining_fields); + self.report_missing_fields(adt_ty, span, remaining_fields.clone()); } } - error_happened + (error_happened, remaining_fields.iter().map(|(ident, _)| ident.clone()).collect()) } fn check_struct_fields_on_error( diff --git a/src/doc/unstable-book/src/language-features/type-changing-struct-update.md b/src/doc/unstable-book/src/language-features/type-changing-struct-update.md new file mode 100644 index 0000000000000..9909cf35b5b51 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/type-changing-struct-update.md @@ -0,0 +1,33 @@ +# `type_changing_struct_update` + +The tracking issue for this feature is: [#86555] + +[#86555]: https://github.com/rust-lang/rust/issues/86555 + +------------------------ + +This implements [RFC2528]. When turned on, you can create instances of the same struct +that have different generic type or lifetime parameters. + +[RFC2528]: https://github.com/rust-lang/rfcs/blob/master/text/2528-type-changing-struct-update-syntax.md + +```rust +#![allow(unused_variables, dead_code)] +#![feature(type_changing_struct_update)] + +fn main () { + struct Foo { + field1: T, + field2: U, + } + + let base: Foo = Foo { + field1: String::from("hello"), + field2: 1234, + }; + let updated: Foo = Foo { + field1: 3.14, + ..base + }; +} +``` diff --git a/src/test/ui/feature-gates/feature-gate-type_changing_struct_update.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs similarity index 92% rename from src/test/ui/feature-gates/feature-gate-type_changing_struct_update.rs rename to src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs index 520c1478f3234..d05ced724cc39 100644 --- a/src/test/ui/feature-gates/feature-gate-type_changing_struct_update.rs +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs @@ -1,3 +1,5 @@ +// gate-test-type_changing_struct_update + #[derive(Debug)] struct Machine { state: S, diff --git a/src/test/ui/feature-gates/feature-gate-type_changing_struct_update.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr similarity index 84% rename from src/test/ui/feature-gates/feature-gate-type_changing_struct_update.stderr rename to src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr index 9934fe6816430..e45ab02a9b7ff 100644 --- a/src/test/ui/feature-gates/feature-gate-type_changing_struct_update.stderr +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/feature-gate-type_changing_struct_update.rs:20:11 + --> $DIR/feature-gate.rs:22:11 | LL | ..m1 | ^^ expected struct `State2`, found struct `State1` diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.rs new file mode 100644 index 0000000000000..df2fef55dd2d8 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.rs @@ -0,0 +1,43 @@ +#![feature(type_changing_struct_update)] +#![allow(incomplete_features)] + +#[derive(Clone)] +struct Machine<'a, S> { + state: S, + lt_str: &'a str, + common_field: i32, +} + +#[derive(Clone)] +struct State1; +#[derive(Clone)] +struct State2; + +fn update_to_state2() { + let s = String::from("hello"); + let m1: Machine = Machine { + state: State1, + lt_str: &s, + //~^ ERROR `s` does not live long enough [E0597] + // FIXME: The error here actually comes from line 34. The + // span of the error message should be corrected to line 34 + common_field: 2, + }; + // update lifetime + let m3: Machine<'static, State1> = Machine { + lt_str: "hello, too", + ..m1.clone() + }; + // update lifetime and type + let m4: Machine<'static, State2> = Machine { + state: State2, + lt_str: "hello, again", + ..m1.clone() + }; + // updating to `static should fail. + let m2: Machine<'static, State1> = Machine { + ..m1 + }; +} + +fn main() {} diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.stderr new file mode 100644 index 0000000000000..5f93ad6e0279e --- /dev/null +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.stderr @@ -0,0 +1,15 @@ +error[E0597]: `s` does not live long enough + --> $DIR/lifetime-update.rs:20:17 + | +LL | lt_str: &s, + | ^^ borrowed value does not live long enough +... +LL | let m2: Machine<'static, State1> = Machine { + | ------------------------ type annotation requires that `s` is borrowed for `'static` +... +LL | } + | - `s` dropped here while still borrowed + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs new file mode 100644 index 0000000000000..d8b1396a692a7 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs @@ -0,0 +1,57 @@ +#![feature(type_changing_struct_update)] +#![allow(incomplete_features)] + +struct Machine<'a, S, M> { + state: S, + message: M, + lt_str: &'a str, + common_field: i32, +} + +struct State1; +struct State2; + +struct Message1; +struct Message2; + +fn update() { + let m1: Machine = Machine { + state: State1, + message: Message1, + lt_str: "hello", + common_field: 2, + }; + // single type update + let m2: Machine = Machine { + state: State2, + ..m1 + }; + // multiple type update + let m3: Machine = Machine { + state: State2, + message: Message2, + ..m1 + }; +} + +fn fail_update() { + let m1: Machine = Machine { + state: 3.2, + message: 6.4, + lt_str: "hello", + common_field: 2, + }; + // single type update fail + let m2: Machine = Machine { + ..m1 + //~^ ERROR mismatched types [E0308] + }; + // multiple type update fail + let m3 = Machine:: { + ..m1 + //~^ ERROR mismatched types [E0308] + //~| ERROR mismatched types [E0308] + }; +} + +fn main() {} diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr new file mode 100644 index 0000000000000..fa8d6ee23d5ec --- /dev/null +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr @@ -0,0 +1,30 @@ +error[E0308]: mismatched types + --> $DIR/type-generic-update.rs:46:11 + | +LL | ..m1 + | ^^ field type mismatch: Machine.state + | + = note: expected type `i32` + found type `f64` + +error[E0308]: mismatched types + --> $DIR/type-generic-update.rs:51:11 + | +LL | ..m1 + | ^^ field type mismatch: Machine.state + | + = note: expected type `i32` + found type `f64` + +error[E0308]: mismatched types + --> $DIR/type-generic-update.rs:51:11 + | +LL | ..m1 + | ^^ field type mismatch: Machine.message + | + = note: expected type `i32` + found type `f64` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. From f3679bc23e0ae5998d5f0141a51b3d084fd66eab Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 29 Oct 2021 11:16:28 +0800 Subject: [PATCH 02/17] move the processing part of `base_expr` into `check_expr_struct_fields` --- compiler/rustc_typeck/src/check/expr.rs | 192 ++++++++---------- .../feature-gate.rs | 2 +- .../feature-gate.stderr | 2 +- 3 files changed, 92 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 25ad6b659a799..4c7c9ed766deb 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -23,7 +23,7 @@ use crate::type_error_struct; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use rustc_ast as ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorReported; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; @@ -1264,109 +1264,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); } - let (error_happened, mut remaining_fields) = self.check_expr_struct_fields( + self.check_expr_struct_fields( adt_ty, expected, expr.hir_id, qpath.span(), variant, fields, - base_expr.is_none(), + base_expr, expr.span, ); - if let Some(base_expr) = base_expr { - // If check_expr_struct_fields hit an error, do not attempt to populate - // the fields with the base_expr. This could cause us to hit errors later - // when certain fields are assumed to exist that in fact do not. - if !error_happened { - // FIXME: We are currently creating two branches here in order to maintain - // consistency. But they should be merged as much as possible. - if self.tcx.features().type_changing_struct_update { - let base_ty = self.check_expr(base_expr); - match (adt_ty.kind(), base_ty.kind()) { - (ty::Adt(adt, substs), ty::Adt(base_adt, base_subs)) if adt == base_adt => { - if !adt.is_struct() { - self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct { - span: base_expr.span, - }); - }; - let fru_field_types = variant - .fields - .iter() - .map(|f| { - let fru_ty = self.normalize_associated_types_in( - expr.span, - self.field_ty(base_expr.span, f, base_subs), - ); - let ident = self.tcx.adjust_ident(f.ident, variant.def_id); - if remaining_fields.remove(&ident) { - let target_ty = self.field_ty(base_expr.span, f, substs); - let cause = self.misc(base_expr.span); - match self.at(&cause, self.param_env).sup(target_ty, fru_ty) - { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations) - } - // FIXME: Needs better diagnostics here - Err(_) => self - .report_mismatched_types( - &cause, - target_ty, - fru_ty, - FieldMisMatch(variant.ident.name, ident.name), - ) - .emit(), - } - } - fru_ty - }) - .collect(); - - self.typeck_results - .borrow_mut() - .fru_field_types_mut() - .insert(expr.hir_id, fru_field_types); - } - _ => { - self.report_mismatched_types( - &self.misc(base_expr.span), - adt_ty, - base_ty, - Mismatch, - ) - .emit(); - } - } - } else { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {}); - match adt_ty.kind() { - ty::Adt(adt, substs) if adt.is_struct() => { - let fru_field_types = adt - .non_enum_variant() - .fields - .iter() - .map(|f| { - self.normalize_associated_types_in( - expr.span, - f.ty(self.tcx, substs), - ) - }) - .collect(); - - self.typeck_results - .borrow_mut() - .fru_field_types_mut() - .insert(expr.hir_id, fru_field_types); - } - _ => { - self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct { - span: base_expr.span, - }); - } - } - }; - } - } + self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized); adt_ty } @@ -1379,9 +1287,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, variant: &'tcx ty::VariantDef, ast_fields: &'tcx [hir::ExprField<'tcx>], - check_completeness: bool, + base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, expr_span: Span, - ) -> (bool, FxHashSet) { + ) { let tcx = self.tcx; let adt_ty_hint = self @@ -1456,7 +1364,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .emit(); } - } else if check_completeness && !error_happened && !remaining_fields.is_empty() { + } + + // If check_expr_struct_fields hit an error, do not attempt to populate + // the fields with the base_expr. This could cause us to hit errors later + // when certain fields are assumed to exist that in fact do not. + if error_happened { + return; + } + + if let Some(base_expr) = base_expr { + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + let fru_tys = if self.tcx.features().type_changing_struct_update { + let base_ty = self.check_expr(base_expr); + match (adt_ty.kind(), base_ty.kind()) { + (ty::Adt(adt, substs), ty::Adt(base_adt, base_subs)) if adt == base_adt => { + if !adt.is_struct() { + self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct { + span: base_expr.span, + }); + }; + variant + .fields + .iter() + .map(|f| { + let fru_ty = self.normalize_associated_types_in( + expr_span, + self.field_ty(base_expr.span, f, base_subs), + ); + let ident = self.tcx.adjust_ident(f.ident, variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + let target_ty = self.field_ty(base_expr.span, f, substs); + let cause = self.misc(base_expr.span); + match self.at(&cause, self.param_env).sup(target_ty, fru_ty) { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations) + } + // FIXME: Need better diagnostics for `FieldMisMatch` error + Err(_) => self + .report_mismatched_types( + &cause, + target_ty, + fru_ty, + FieldMisMatch(variant.ident.name, ident.name), + ) + .emit(), + } + } + fru_ty + }) + .collect() + } + _ => { + return self + .report_mismatched_types( + &self.misc(base_expr.span), + adt_ty, + base_ty, + Mismatch, + ) + .emit(); + } + } + } else { + self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {}); + match adt_ty.kind() { + ty::Adt(adt, substs) if adt.is_struct() => variant + .fields + .iter() + .map(|f| { + self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs)) + }) + .collect(), + _ => { + return self + .tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); + } + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); + } else if kind_name != "union" && !remaining_fields.is_empty() { let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| { !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) }); @@ -1464,11 +1454,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if inaccessible_remaining_fields { self.report_inaccessible_fields(adt_ty, span); } else { - self.report_missing_fields(adt_ty, span, remaining_fields.clone()); + self.report_missing_fields(adt_ty, span, remaining_fields); } } - - (error_happened, remaining_fields.iter().map(|(ident, _)| ident.clone()).collect()) } fn check_struct_fields_on_error( diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs index d05ced724cc39..81888ee1d4fdc 100644 --- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs @@ -17,11 +17,11 @@ fn update_to_state2() { common_field1: "hello", common_field2: 2, }; + // FIXME: this should trigger feature gate let m2: Machine = Machine { state: State2, ..m1 //~ ERROR mismatched types }; - // FIXME: this should trigger feature gate assert_eq!(State2, m2.state); } diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr index e45ab02a9b7ff..19059593844d6 100644 --- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/feature-gate.rs:22:11 + --> $DIR/feature-gate.rs:23:11 | LL | ..m1 | ^^ expected struct `State2`, found struct `State1` From 1ab2616b4da64218d359a87fdd043edf4167cf76 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 29 Oct 2021 20:08:30 +0800 Subject: [PATCH 03/17] Add feature trigger and correct `is_struct` check --- compiler/rustc_typeck/src/check/expr.rs | 177 +++++++++++------- .../feature-gate.rs | 5 +- .../feature-gate.stderr | 16 +- .../type-generic-update.rs | 1 - .../type-generic-update.stderr | 11 +- 5 files changed, 129 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 4c7c9ed766deb..1d4a43c07b1e6 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -36,11 +36,12 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::InferOk; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::TypeError::{FieldMisMatch, Mismatch}; +use rustc_middle::ty::error::TypeError::FieldMisMatch; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{AdtKind, Visibility}; +use rustc_session::parse::feature_err; use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; @@ -1374,78 +1375,124 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(base_expr) = base_expr { - // FIXME: We are currently creating two branches here in order to maintain - // consistency. But they should be merged as much as possible. - let fru_tys = if self.tcx.features().type_changing_struct_update { - let base_ty = self.check_expr(base_expr); - match (adt_ty.kind(), base_ty.kind()) { - (ty::Adt(adt, substs), ty::Adt(base_adt, base_subs)) if adt == base_adt => { - if !adt.is_struct() { - self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct { - span: base_expr.span, - }); - }; + let expected = if self.tcx.features().type_changing_struct_update { + NoExpectation + } else { + ExpectHasType(adt_ty) + }; + let mut ty = self.check_expr_with_expectation(base_expr, expected); + + let expected_ty = expected.to_option(&self).unwrap_or(adt_ty); + // While we don't allow *arbitrary* coercions here, we *do* allow + // coercions from ! to `expected`. + if ty.is_never() { + assert!( + !self.typeck_results.borrow().adjustments().contains_key(base_expr.hir_id), + "expression with never type wound up being adjusted" + ); + let adj_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::AdjustmentType, + span: base_expr.span, + }); + self.apply_adjustments( + base_expr, + vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }], + ); + ty = adj_ty; + } + let cause = self.misc(base_expr.span); + let mut fru_tys = None; + let mut err = None; + let is_struct; + + if let ty::Adt(adt, substs) = expected_ty.kind() { + match ty.kind() { + ty::Adt(base_adt, base_subs) if adt == base_adt => { + if self.tcx.features().type_changing_struct_update { + let tys = variant + .fields + .iter() + .map(|f| { + let fru_ty = self.normalize_associated_types_in( + expr_span, + self.field_ty(base_expr.span, f, base_subs), + ); + let ident = self.tcx.adjust_ident(f.ident, variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + let target_ty = self.field_ty(base_expr.span, f, substs); + match self.at(&cause, self.param_env).sup(target_ty, fru_ty) + { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations) + } + // FIXME: Need better diagnostics for `FieldMisMatch` error + Err(_) => { + if err.is_none() { + err = Some(self.report_mismatched_types( + &cause, + target_ty, + fru_ty, + FieldMisMatch( + variant.ident.name, + ident.name, + ), + )) + } + } + } + } + fru_ty + }) + .collect(); + fru_tys = Some(tys); + } else { + err = self.demand_suptype_diag(base_expr.span, expected_ty, ty); + if err.is_some() && self.tcx.sess.is_nightly_build() { + feature_err( + &self.tcx.sess.parse_sess, + sym::type_changing_struct_update, + base_expr.span, + "type changing struct updating is experimental", + ) + .emit(); + } + } + } + _ => { + err = self.demand_suptype_diag(base_expr.span, expected_ty, ty); + } + } + is_struct = adt.is_struct(); + if is_struct && fru_tys.is_none() { + fru_tys = Some( variant .fields .iter() .map(|f| { - let fru_ty = self.normalize_associated_types_in( + self.normalize_associated_types_in( expr_span, - self.field_ty(base_expr.span, f, base_subs), - ); - let ident = self.tcx.adjust_ident(f.ident, variant.def_id); - if let Some(_) = remaining_fields.remove(&ident) { - let target_ty = self.field_ty(base_expr.span, f, substs); - let cause = self.misc(base_expr.span); - match self.at(&cause, self.param_env).sup(target_ty, fru_ty) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations) - } - // FIXME: Need better diagnostics for `FieldMisMatch` error - Err(_) => self - .report_mismatched_types( - &cause, - target_ty, - fru_ty, - FieldMisMatch(variant.ident.name, ident.name), - ) - .emit(), - } - } - fru_ty + f.ty(self.tcx, substs), + ) }) - .collect() - } - _ => { - return self - .report_mismatched_types( - &self.misc(base_expr.span), - adt_ty, - base_ty, - Mismatch, - ) - .emit(); - } + .collect(), + ) } } else { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {}); - match adt_ty.kind() { - ty::Adt(adt, substs) if adt.is_struct() => variant - .fields - .iter() - .map(|f| { - self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs)) - }) - .collect(), - _ => { - return self - .tcx - .sess - .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); - } - } - }; - self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); + err = self.demand_suptype_diag(base_expr.span, expected_ty, ty); + is_struct = false; + } + if let Some(mut err) = err { + let expr = base_expr.peel_drop_temps(); + self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None); + err.emit(); + } + if let Some(fru_tys) = fru_tys { + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); + } + if !is_struct { + let e = FunctionalRecordUpdateOnNonStruct { span: base_expr.span }; + self.tcx.sess.emit_err(e); + } } else if kind_name != "union" && !remaining_fields.is_empty() { let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| { !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs index 81888ee1d4fdc..1e8b99ba5647a 100644 --- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs @@ -17,10 +17,11 @@ fn update_to_state2() { common_field1: "hello", common_field2: 2, }; - // FIXME: this should trigger feature gate let m2: Machine = Machine { state: State2, - ..m1 //~ ERROR mismatched types + ..m1 + //~^ ERROR type changing struct updating is experimental [E0658] + //~| ERROR mismatched types [E0308] }; assert_eq!(State2, m2.state); } diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr index 19059593844d6..2217b8c049863 100644 --- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr @@ -1,5 +1,14 @@ +error[E0658]: type changing struct updating is experimental + --> $DIR/feature-gate.rs:22:11 + | +LL | ..m1 + | ^^ + | + = note: see issue #86555 for more information + = help: add `#![feature(type_changing_struct_update)]` to the crate attributes to enable + error[E0308]: mismatched types - --> $DIR/feature-gate.rs:23:11 + --> $DIR/feature-gate.rs:22:11 | LL | ..m1 | ^^ expected struct `State2`, found struct `State1` @@ -7,6 +16,7 @@ LL | ..m1 = note: expected struct `Machine` found struct `Machine` -error: aborting due to previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs index d8b1396a692a7..dae1241d35a5f 100644 --- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs @@ -50,7 +50,6 @@ fn fail_update() { let m3 = Machine:: { ..m1 //~^ ERROR mismatched types [E0308] - //~| ERROR mismatched types [E0308] }; } diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr index fa8d6ee23d5ec..631c8f83c91e7 100644 --- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr @@ -16,15 +16,6 @@ LL | ..m1 = note: expected type `i32` found type `f64` -error[E0308]: mismatched types - --> $DIR/type-generic-update.rs:51:11 - | -LL | ..m1 - | ^^ field type mismatch: Machine.message - | - = note: expected type `i32` - found type `f64` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. From 926892ddc0b75b50f5d0a3483d829e501aa8e895 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 5 Nov 2021 09:30:49 +0800 Subject: [PATCH 04/17] Add feature trigger and enable is_struct check --- compiler/rustc_typeck/src/check/expr.rs | 188 ++++++++---------- .../type-generic-update.rs | 1 + .../type-generic-update.stderr | 11 +- 3 files changed, 96 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 1d4a43c07b1e6..ab53d64e9bfb5 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -36,7 +36,8 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::InferOk; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::TypeError::FieldMisMatch; +use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts}; +use rustc_middle::ty::relate::expected_found_bool; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; @@ -1375,60 +1376,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(base_expr) = base_expr { - let expected = if self.tcx.features().type_changing_struct_update { - NoExpectation - } else { - ExpectHasType(adt_ty) - }; - let mut ty = self.check_expr_with_expectation(base_expr, expected); - - let expected_ty = expected.to_option(&self).unwrap_or(adt_ty); - // While we don't allow *arbitrary* coercions here, we *do* allow - // coercions from ! to `expected`. - if ty.is_never() { - assert!( - !self.typeck_results.borrow().adjustments().contains_key(base_expr.hir_id), - "expression with never type wound up being adjusted" - ); - let adj_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::AdjustmentType, - span: base_expr.span, - }); - self.apply_adjustments( - base_expr, - vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }], - ); - ty = adj_ty; - } - let cause = self.misc(base_expr.span); - let mut fru_tys = None; - let mut err = None; - let is_struct; - - if let ty::Adt(adt, substs) = expected_ty.kind() { - match ty.kind() { - ty::Adt(base_adt, base_subs) if adt == base_adt => { - if self.tcx.features().type_changing_struct_update { - let tys = variant - .fields - .iter() - .map(|f| { - let fru_ty = self.normalize_associated_types_in( - expr_span, - self.field_ty(base_expr.span, f, base_subs), - ); - let ident = self.tcx.adjust_ident(f.ident, variant.def_id); - if let Some(_) = remaining_fields.remove(&ident) { - let target_ty = self.field_ty(base_expr.span, f, substs); - match self.at(&cause, self.param_env).sup(target_ty, fru_ty) - { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations) - } - // FIXME: Need better diagnostics for `FieldMisMatch` error - Err(_) => { - if err.is_none() { - err = Some(self.report_mismatched_types( + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + let fru_tys = if self.tcx.features().type_changing_struct_update { + let base_ty = self.check_expr(base_expr); + match adt_ty.kind() { + ty::Adt(adt, substs) if adt.is_struct() => { + match base_ty.kind() { + ty::Adt(base_adt, base_subs) if adt == base_adt => { + variant + .fields + .iter() + .map(|f| { + let fru_ty = self.normalize_associated_types_in( + expr_span, + self.field_ty(base_expr.span, f, base_subs), + ); + let ident = self.tcx.adjust_ident(f.ident, variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + let target_ty = + self.field_ty(base_expr.span, f, substs); + let cause = self.misc(base_expr.span); + match self + .at(&cause, self.param_env) + .sup(target_ty, fru_ty) + { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations) + } + // FIXME: Need better diagnostics for `FieldMisMatch` error + Err(_) => self + .report_mismatched_types( &cause, target_ty, fru_ty, @@ -1436,63 +1414,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant.ident.name, ident.name, ), - )) - } + ) + .emit(), } } - } - fru_ty - }) - .collect(); - fru_tys = Some(tys); - } else { - err = self.demand_suptype_diag(base_expr.span, expected_ty, ty); - if err.is_some() && self.tcx.sess.is_nightly_build() { - feature_err( - &self.tcx.sess.parse_sess, - sym::type_changing_struct_update, - base_expr.span, - "type changing struct updating is experimental", - ) - .emit(); + fru_ty + }) + .collect() + } + _ => { + return self + .report_mismatched_types( + &self.misc(base_expr.span), + adt_ty, + base_ty, + Sorts(expected_found_bool(true, adt_ty, base_ty)), + ) + .emit(); } } } _ => { - err = self.demand_suptype_diag(base_expr.span, expected_ty, ty); + return self + .tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); } } - is_struct = adt.is_struct(); - if is_struct && fru_tys.is_none() { - fru_tys = Some( - variant - .fields - .iter() - .map(|f| { - self.normalize_associated_types_in( - expr_span, - f.ty(self.tcx, substs), - ) - }) - .collect(), - ) - } } else { - err = self.demand_suptype_diag(base_expr.span, expected_ty, ty); - is_struct = false; - } - if let Some(mut err) = err { - let expr = base_expr.peel_drop_temps(); - self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None); - err.emit(); - } - if let Some(fru_tys) = fru_tys { - self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); - } - if !is_struct { - let e = FunctionalRecordUpdateOnNonStruct { span: base_expr.span }; - self.tcx.sess.emit_err(e); - } + self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { + let base_ty = self.check_expr(base_expr); + let same_adt = match (adt_ty.kind(), base_ty.kind()) { + (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true, + _ => false, + }; + if self.tcx.sess.is_nightly_build() && same_adt { + feature_err( + &self.tcx.sess.parse_sess, + sym::type_changing_struct_update, + base_expr.span, + "type changing struct updating is experimental", + ) + .emit(); + } + }); + match adt_ty.kind() { + ty::Adt(adt, substs) if adt.is_struct() => variant + .fields + .iter() + .map(|f| { + self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs)) + }) + .collect(), + _ => { + return self + .tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); + } + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); } else if kind_name != "union" && !remaining_fields.is_empty() { let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| { !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs index dae1241d35a5f..d8b1396a692a7 100644 --- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs @@ -50,6 +50,7 @@ fn fail_update() { let m3 = Machine:: { ..m1 //~^ ERROR mismatched types [E0308] + //~| ERROR mismatched types [E0308] }; } diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr index 631c8f83c91e7..fa8d6ee23d5ec 100644 --- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr @@ -16,6 +16,15 @@ LL | ..m1 = note: expected type `i32` found type `f64` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/type-generic-update.rs:51:11 + | +LL | ..m1 + | ^^ field type mismatch: Machine.message + | + = note: expected type `i32` + found type `f64` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. From 02c1774cd3e4239c26b67ae78a21a87f4e460fad Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 2 Oct 2021 12:59:26 +0100 Subject: [PATCH 05/17] Give inline const separate DefKind --- .../rustc_const_eval/src/const_eval/eval_queries.rs | 1 + compiler/rustc_hir/src/def.rs | 7 ++++++- compiler/rustc_metadata/src/rmeta/encoder.rs | 12 +++++++++--- compiler/rustc_middle/src/hir/map/mod.rs | 10 +++++++++- compiler/rustc_middle/src/mir/pretty.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 3 ++- compiler/rustc_monomorphize/src/polymorphize.rs | 3 ++- compiler/rustc_privacy/src/lib.rs | 1 + compiler/rustc_resolve/src/build_reduced_graph.rs | 1 + compiler/rustc_resolve/src/late/lifetimes.rs | 2 +- compiler/rustc_save_analysis/src/lib.rs | 1 + .../src/traits/const_evaluatable.rs | 4 ++-- src/librustdoc/clean/utils.rs | 5 +++-- src/librustdoc/formats/item_type.rs | 1 + src/librustdoc/passes/collect_intra_doc_links.rs | 3 ++- src/tools/clippy/clippy_lints/src/matches.rs | 5 ++++- 16 files changed, 46 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 57af0ff071433..6d3a89c0a8a5b 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -42,6 +42,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( | DefKind::Static | DefKind::ConstParam | DefKind::AnonConst + | DefKind::InlineConst | DefKind::AssocConst ), "Unexpected DefKind: {:?}", diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index cb668eb35e093..60761a05de827 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -104,8 +104,10 @@ pub enum DefKind { Use, /// An `extern` block. ForeignMod, - /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`, or `const { 1 + 2}` + /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]` AnonConst, + /// An inline constant, e.g. `const { 1 + 2 }` + InlineConst, /// Opaque type, aka `impl Trait`. OpaqueTy, Field, @@ -155,6 +157,7 @@ impl DefKind { DefKind::Use => "import", DefKind::ForeignMod => "foreign module", DefKind::AnonConst => "constant expression", + DefKind::InlineConst => "inline constant", DefKind::Field => "field", DefKind::Impl => "implementation", DefKind::Closure => "closure", @@ -174,6 +177,7 @@ impl DefKind { | DefKind::OpaqueTy | DefKind::Impl | DefKind::Use + | DefKind::InlineConst | DefKind::ExternCrate => "an", DefKind::Macro(macro_kind) => macro_kind.article(), _ => "a", @@ -207,6 +211,7 @@ impl DefKind { // Not namespaced. DefKind::AnonConst + | DefKind::InlineConst | DefKind::Field | DefKind::LifetimeParam | DefKind::ExternCrate diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 0dbef66ac37d7..aff5e02d0c655 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -797,6 +797,7 @@ fn should_encode_visibility(def_kind: DefKind) -> bool { | DefKind::ConstParam | DefKind::LifetimeParam | DefKind::AnonConst + | DefKind::InlineConst | DefKind::GlobalAsm | DefKind::Closure | DefKind::Generator @@ -832,6 +833,7 @@ fn should_encode_stability(def_kind: DefKind) -> bool { DefKind::Use | DefKind::LifetimeParam | DefKind::AnonConst + | DefKind::InlineConst | DefKind::GlobalAsm | DefKind::Closure | DefKind::Generator @@ -856,9 +858,11 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) { (true, mir_opt_base) } // Constants - DefKind::AnonConst | DefKind::AssocConst | DefKind::Static | DefKind::Const => { - (true, false) - } + DefKind::AnonConst + | DefKind::InlineConst + | DefKind::AssocConst + | DefKind::Static + | DefKind::Const => (true, false), // Full-fledged functions DefKind::AssocFn | DefKind::Fn => { let generics = tcx.generics_of(def_id); @@ -914,6 +918,7 @@ fn should_encode_variances(def_kind: DefKind) -> bool { | DefKind::Use | DefKind::LifetimeParam | DefKind::AnonConst + | DefKind::InlineConst | DefKind::GlobalAsm | DefKind::Closure | DefKind::Generator @@ -939,6 +944,7 @@ fn should_encode_generics(def_kind: DefKind) -> bool { | DefKind::AssocFn | DefKind::AssocConst | DefKind::AnonConst + | DefKind::InlineConst | DefKind::OpaqueTy | DefKind::Impl | DefKind::Field diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 8f52e16c2ebe4..d9d0781b37aac 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -266,7 +266,15 @@ impl<'hir> Map<'hir> { }; DefKind::Ctor(ctor_of, def::CtorKind::from_hir(variant_data)) } - Node::AnonConst(_) => DefKind::AnonConst, + Node::AnonConst(_) => { + let inline = match self.find(self.get_parent_node(hir_id)) { + Some(Node::Expr(&Expr { + kind: ExprKind::ConstBlock(ref anon_const), .. + })) if anon_const.hir_id == hir_id => true, + _ => false, + }; + if inline { DefKind::InlineConst } else { DefKind::AnonConst } + } Node::Field(_) => DefKind::Field, Node::Expr(expr) => match expr.kind { ExprKind::Closure(.., None) => DefKind::Closure, diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8e363cfbff562..8e1b887f87da7 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -958,7 +958,7 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Res write!(w, "static {}", if tcx.is_mutable_static(def_id) { "mut " } else { "" })? } (_, _) if is_function => write!(w, "fn ")?, - (DefKind::AnonConst, _) => {} // things like anon const, not an item + (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item _ => bug!("Unexpected def kind {:?}", kind), } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index cf47da157d19f..26bae1e15a372 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1927,7 +1927,8 @@ impl<'tcx> TyCtxt<'tcx> { | DefKind::Static | DefKind::AssocConst | DefKind::Ctor(..) - | DefKind::AnonConst => self.mir_for_ctfe_opt_const_arg(def), + | DefKind::AnonConst + | DefKind::InlineConst => self.mir_for_ctfe_opt_const_arg(def), // If the caller wants `mir_for_ctfe` of a function they should not be using // `instance_mir`, so we'll assume const fn also wants the optimized version. _ => { diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index e6e4438b6d41a..ba62a6eea10ea 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -167,6 +167,7 @@ fn mark_used_by_default_parameters<'tcx>( | DefKind::Use | DefKind::ForeignMod | DefKind::AnonConst + | DefKind::InlineConst | DefKind::OpaqueTy | DefKind::Field | DefKind::LifetimeParam @@ -303,7 +304,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { ControlFlow::CONTINUE } ty::ConstKind::Unevaluated(uv) - if self.tcx.def_kind(uv.def.did) == DefKind::AnonConst => + if matches!(self.tcx.def_kind(uv.def.did), DefKind::AnonConst | DefKind::InlineConst) => { self.visit_child_body(uv.def.did, uv.substs(self.tcx)); ControlFlow::CONTINUE diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index fa34b9abc1e6c..a01efc5d85c6e 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -618,6 +618,7 @@ impl EmbargoVisitor<'tcx> { | DefKind::Use | DefKind::ForeignMod | DefKind::AnonConst + | DefKind::InlineConst | DefKind::Field | DefKind::GlobalAsm | DefKind::Impl diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 33af9884cbb66..44268877cb0a5 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -967,6 +967,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { | DefKind::Use | DefKind::ForeignMod | DefKind::AnonConst + | DefKind::InlineConst | DefKind::Field | DefKind::LifetimeParam | DefKind::GlobalAsm diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 94563400a8b53..ed9100024def8 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -540,7 +540,7 @@ fn is_late_bound_map<'tcx>( def_id: LocalDefId, ) -> Option<(LocalDefId, &'tcx FxHashSet)> { match tcx.def_kind(def_id) { - DefKind::AnonConst => { + DefKind::AnonConst | DefKind::InlineConst => { let mut def_id = tcx .parent(def_id.to_def_id()) .unwrap_or_else(|| bug!("anon const or closure without a parent")); diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 543cd0247a53d..c7f8fe3a88a64 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -739,6 +739,7 @@ impl<'tcx> SaveContext<'tcx> { | HirDefKind::ForeignMod | HirDefKind::LifetimeParam | HirDefKind::AnonConst + | HirDefKind::InlineConst | HirDefKind::Use | HirDefKind::Field | HirDefKind::GlobalAsm diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 8edb7069fc45f..6b5d37c0f4308 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -151,7 +151,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( if concrete.is_ok() && uv.substs(infcx.tcx).definitely_has_param_types_or_consts(infcx.tcx) { match infcx.tcx.def_kind(uv.def.did) { - DefKind::AnonConst => { + DefKind::AnonConst | DefKind::InlineConst => { let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def); if mir_body.is_polymorphic { @@ -495,7 +495,7 @@ pub(super) fn thir_abstract_const<'tcx>( // we want to look into them or treat them as opaque projections. // // Right now we do neither of that and simply always fail to unify them. - DefKind::AnonConst => (), + DefKind::AnonConst | DefKind::InlineConst => (), _ => return Ok(None), } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 2fae3163a1a1a..2fa7efcc6509b 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -430,8 +430,9 @@ crate fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId { | Res::NonMacroAttr(_) | Res::Err => return res.def_id(), Res::Def( - TyParam | ConstParam | Ctor(..) | ExternCrate | Use | ForeignMod | AnonConst | OpaqueTy - | Field | LifetimeParam | GlobalAsm | Impl | Closure | Generator, + TyParam | ConstParam | Ctor(..) | ExternCrate | Use | ForeignMod | AnonConst + | InlineConst | OpaqueTy | Field | LifetimeParam | GlobalAsm | Impl | Closure + | Generator, id, ) => return id, }; diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index 793db16faf385..3979d29b673fd 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -134,6 +134,7 @@ impl From for ItemType { | DefKind::Use | DefKind::ForeignMod | DefKind::AnonConst + | DefKind::InlineConst | DefKind::OpaqueTy | DefKind::Field | DefKind::LifetimeParam diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 8541e6e18816f..4e5812d7f8429 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1937,7 +1937,8 @@ fn resolution_failure( | Use | LifetimeParam | Ctor(_, _) - | AnonConst => { + | AnonConst + | InlineConst => { let note = assoc_item_not_allowed(res); if let Some(span) = sp { diag.span_label(span, ¬e); diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index eb311983b2927..7142df98c3f10 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -1065,7 +1065,10 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) PatKind::Path(path) => { #[allow(clippy::match_same_arms)] let id = match cx.qpath_res(path, pat.hir_id) { - Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return, + Res::Def( + DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst, + _, + ) => return, Res::Def(_, id) => id, _ => return, }; From 468192a9c52e613f56af5b2a967d33c326cbf373 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 2 Oct 2021 13:12:33 +0100 Subject: [PATCH 06/17] Implement type inference for inline consts In most cases it is handled in the same way as closures. --- .../rustc_borrowck/src/region_infer/mod.rs | 3 +- compiler/rustc_borrowck/src/type_check/mod.rs | 14 +++- .../rustc_borrowck/src/universal_regions.rs | 49 +++++++++--- compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_middle/src/ty/consts.rs | 79 ++++++++++++++++--- compiler/rustc_middle/src/ty/mod.rs | 7 +- compiler/rustc_middle/src/ty/sty.rs | 60 ++++++++++++++ compiler/rustc_middle/src/ty/subst.rs | 10 ++- compiler/rustc_middle/src/ty/util.rs | 23 ++++-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 2 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 2 +- compiler/rustc_passes/src/region.rs | 5 +- compiler/rustc_typeck/src/check/expr.rs | 23 +++++- compiler/rustc_typeck/src/check/pat.rs | 4 +- compiler/rustc_typeck/src/check/regionck.rs | 28 +++++++ compiler/rustc_typeck/src/check/writeback.rs | 6 ++ compiler/rustc_typeck/src/collect.rs | 22 +++++- compiler/rustc_typeck/src/collect/type_of.rs | 3 +- 18 files changed, 299 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f4a5da1fe36fa..053e5c33313cc 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -569,7 +569,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // to store those. Otherwise, we'll pass in `None` to the // functions below, which will trigger them to report errors // eagerly. - let mut outlives_requirements = infcx.tcx.is_closure(mir_def_id).then(Vec::new); + let mut outlives_requirements = + infcx.tcx.is_closure_or_inline_const(mir_def_id).then(Vec::new); self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 7e6a481ca69a1..9e068ef716504 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1345,7 +1345,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Some(RETURN_PLACE) => { if let BorrowCheckContext { universal_regions: - UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. }, + UniversalRegions { + defining_ty: + DefiningTy::Const(def_id, _) + | DefiningTy::InlineConst(def_id, _), + .. + }, .. } = self.borrowck_context { @@ -1650,7 +1655,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Some(RETURN_PLACE) => { if let BorrowCheckContext { universal_regions: - UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. }, + UniversalRegions { + defining_ty: + DefiningTy::Const(def_id, _) + | DefiningTy::InlineConst(def_id, _), + .. + }, .. } = self.borrowck_context { diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 147e2aead648d..ab8725834376c 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -23,7 +23,7 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; +use rustc_middle::ty::{self, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt}; use std::iter; use crate::nll::ToRegionVid; @@ -108,6 +108,10 @@ pub enum DefiningTy<'tcx> { /// is that it has no inputs and a single return value, which is /// the value of the constant. Const(DefId, SubstsRef<'tcx>), + + /// The MIR represents an inline const. The signature has no inputs and a + /// single return value found via `InlineConstSubsts::ty`. + InlineConst(DefId, SubstsRef<'tcx>), } impl<'tcx> DefiningTy<'tcx> { @@ -121,7 +125,7 @@ impl<'tcx> DefiningTy<'tcx> { DefiningTy::Generator(_, substs, _) => { Either::Right(Either::Left(substs.as_generator().upvar_tys())) } - DefiningTy::FnDef(..) | DefiningTy::Const(..) => { + DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => { Either::Right(Either::Right(iter::empty())) } } @@ -133,7 +137,7 @@ impl<'tcx> DefiningTy<'tcx> { pub fn implicit_inputs(self) -> usize { match self { DefiningTy::Closure(..) | DefiningTy::Generator(..) => 1, - DefiningTy::FnDef(..) | DefiningTy::Const(..) => 0, + DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => 0, } } @@ -142,7 +146,7 @@ impl<'tcx> DefiningTy<'tcx> { } pub fn is_const(&self) -> bool { - matches!(*self, DefiningTy::Const(..)) + matches!(*self, DefiningTy::Const(..) | DefiningTy::InlineConst(..)) } pub fn def_id(&self) -> DefId { @@ -150,7 +154,8 @@ impl<'tcx> DefiningTy<'tcx> { DefiningTy::Closure(def_id, ..) | DefiningTy::Generator(def_id, ..) | DefiningTy::FnDef(def_id, ..) - | DefiningTy::Const(def_id, ..) => def_id, + | DefiningTy::Const(def_id, ..) + | DefiningTy::InlineConst(def_id, ..) => def_id, } } } @@ -376,6 +381,12 @@ impl<'tcx> UniversalRegions<'tcx> { tcx.def_path_str_with_substs(def_id, substs), )); } + DefiningTy::InlineConst(def_id, substs) => { + err.note(&format!( + "defining inline constant type: {}", + tcx.def_path_str_with_substs(def_id, substs), + )); + } } } } @@ -534,11 +545,21 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { } BodyOwnerKind::Const | BodyOwnerKind::Static(..) => { - assert_eq!(self.mir_def.did.to_def_id(), closure_base_def_id); let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id); - let substs = - self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs); - DefiningTy::Const(self.mir_def.did.to_def_id(), substs) + if self.mir_def.did.to_def_id() == closure_base_def_id { + let substs = + self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs); + DefiningTy::Const(self.mir_def.did.to_def_id(), substs) + } else { + let ty = tcx.typeck(self.mir_def.did).node_type(self.mir_hir_id); + let substs = InlineConstSubsts::new( + tcx, + InlineConstSubstsParts { parent_substs: identity_substs, ty }, + ) + .substs; + let substs = self.infcx.replace_free_regions_with_nll_infer_vars(FR, substs); + DefiningTy::InlineConst(self.mir_def.did.to_def_id(), substs) + } } } } @@ -556,7 +577,9 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let closure_base_def_id = tcx.closure_base_def_id(self.mir_def.did.to_def_id()); let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id); let fr_substs = match defining_ty { - DefiningTy::Closure(_, ref substs) | DefiningTy::Generator(_, ref substs, _) => { + DefiningTy::Closure(_, ref substs) + | DefiningTy::Generator(_, ref substs, _) + | DefiningTy::InlineConst(_, ref substs) => { // In the case of closures, we rely on the fact that // the first N elements in the ClosureSubsts are // inherited from the `closure_base_def_id`. @@ -648,6 +671,12 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let ty = indices.fold_to_region_vids(tcx, ty); ty::Binder::dummy(tcx.intern_type_list(&[ty])) } + + DefiningTy::InlineConst(def_id, substs) => { + assert_eq!(self.mir_def.did.to_def_id(), def_id); + let ty = substs.as_inline_const().ty(); + ty::Binder::dummy(tcx.intern_type_list(&[ty])) + } } } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 06041bbb02d35..b0952d346766c 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -797,7 +797,7 @@ rustc_queries! { /// additional requirements that the closure's creator must verify. query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> { desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if(tcx) { tcx.is_closure(key.to_def_id()) } + cache_on_disk_if(tcx) { tcx.is_closure_or_inline_const(key.to_def_id()) } } query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> { desc { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 869b2ab9dbcbc..080ef6cb82a0f 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -1,7 +1,9 @@ use crate::mir::interpret::ConstValue; use crate::mir::interpret::{LitToConstInput, Scalar}; -use crate::ty::{self, Ty, TyCtxt}; -use crate::ty::{ParamEnv, ParamEnvAnd}; +use crate::ty::{ + self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, + TyCtxt, TypeFoldable, +}; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -54,6 +56,24 @@ impl<'tcx> Const<'tcx> { let ty = tcx.type_of(def.def_id_for_type_of()); + match Self::try_eval_body_expr(tcx, ty, expr) { + Some(v) => v, + None => tcx.mk_const(ty::Const { + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def: def.to_global(), + substs_: None, + promoted: None, + }), + ty, + }), + } + } + + fn try_eval_body_expr( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Option<&'tcx Self> { let lit_input = match expr.kind { hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind { @@ -69,7 +89,7 @@ impl<'tcx> Const<'tcx> { // If an error occurred, ignore that it's a literal and leave reporting the error up to // mir. if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) { - return c; + return Some(c); } else { tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const"); } @@ -85,7 +105,7 @@ impl<'tcx> Const<'tcx> { }; use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath}; - let val = match expr.kind { + match expr.kind { ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { // Find the name and index of the const parameter by indexing the generics of // the parent item and construct a `ParamConst`. @@ -95,16 +115,53 @@ impl<'tcx> Const<'tcx> { let generics = tcx.generics_of(item_def_id.to_def_id()); let index = generics.param_def_id_to_index[&def_id]; let name = tcx.hir().name(hir_id); - ty::ConstKind::Param(ty::ParamConst::new(index, name)) + Some(tcx.mk_const(ty::Const { + val: ty::ConstKind::Param(ty::ParamConst::new(index, name)), + ty, + })) } - _ => ty::ConstKind::Unevaluated(ty::Unevaluated { - def: def.to_global(), - substs_: None, - promoted: None, - }), + _ => None, + } + } + + pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self { + debug!("Const::from_inline_const(def_id={:?})", def_id); + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + let body_id = match tcx.hir().get(hir_id) { + hir::Node::AnonConst(ac) => ac.body, + _ => span_bug!( + tcx.def_span(def_id.to_def_id()), + "from_inline_const can only process anonymous constants" + ), }; - tcx.mk_const(ty::Const { val, ty }) + let expr = &tcx.hir().body(body_id).value; + + let ty = tcx.typeck(def_id).node_type(hir_id); + + let ret = match Self::try_eval_body_expr(tcx, ty, expr) { + Some(v) => v, + None => { + let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()); + let parent_substs = + tcx.erase_regions(InternalSubsts::identity_for_item(tcx, outer_def_id)); + let substs = + InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty }) + .substs; + tcx.mk_const(ty::Const { + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def: ty::WithOptConstParam::unknown(def_id).to_global(), + substs_: Some(substs), + promoted: None, + }), + ty, + }) + } + }; + debug_assert!(!ret.has_free_regions(tcx)); + ret } /// Interns the given value as a constant. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 26bae1e15a372..40670f1fdcaef 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -74,9 +74,10 @@ pub use self::sty::{ Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind, CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBoundRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig, - GeneratorSubsts, GeneratorSubstsParts, ParamConst, ParamTy, PolyExistentialProjection, - PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, ProjectionTy, Region, RegionKind, - RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind, + GeneratorSubsts, GeneratorSubstsParts, InlineConstSubsts, InlineConstSubstsParts, ParamConst, + ParamTy, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyGenSig, + PolyTraitRef, ProjectionTy, Region, RegionKind, RegionVid, TraitRef, TyKind, TypeAndMut, + UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind, }; pub use self::trait_def::TraitDef; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 874de3366d792..e57075ed33811 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -704,6 +704,66 @@ impl<'tcx> UpvarSubsts<'tcx> { } } +/// An inline const is modeled like +/// +/// const InlineConst<'l0...'li, T0...Tj, R>: R; +/// +/// where: +/// +/// - 'l0...'li and T0...Tj are the generic parameters +/// inherited from the item that defined the inline const, +/// - R represents the type of the constant. +/// +/// When the inline const is instantiated, `R` is substituted as the actual inferred +/// type of the constant. The reason that `R` is represented as an extra type parameter +/// is the same reason that [`ClosureSubsts`] have `CS` and `U` as type parameters: +/// inline const can reference lifetimes that are internal to the creating function. +#[derive(Copy, Clone, Debug, TypeFoldable)] +pub struct InlineConstSubsts<'tcx> { + /// Generic parameters from the enclosing item, + /// concatenated with the inferred type of the constant. + pub substs: SubstsRef<'tcx>, +} + +/// Struct returned by `split()`. +pub struct InlineConstSubstsParts<'tcx, T> { + pub parent_substs: &'tcx [GenericArg<'tcx>], + pub ty: T, +} + +impl<'tcx> InlineConstSubsts<'tcx> { + /// Construct `InlineConstSubsts` from `InlineConstSubstsParts`. + pub fn new( + tcx: TyCtxt<'tcx>, + parts: InlineConstSubstsParts<'tcx, Ty<'tcx>>, + ) -> InlineConstSubsts<'tcx> { + InlineConstSubsts { + substs: tcx.mk_substs( + parts.parent_substs.iter().copied().chain(std::iter::once(parts.ty.into())), + ), + } + } + + /// Divides the inline const substs into their respective components. + /// The ordering assumed here must match that used by `InlineConstSubsts::new` above. + fn split(self) -> InlineConstSubstsParts<'tcx, GenericArg<'tcx>> { + match self.substs[..] { + [ref parent_substs @ .., ty] => InlineConstSubstsParts { parent_substs, ty }, + _ => bug!("inline const substs missing synthetics"), + } + } + + /// Returns the substitutions of the inline const's parent. + pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] { + self.split().parent_substs + } + + /// Returns the type of this inline const. + pub fn ty(self) -> Ty<'tcx> { + self.split().ty.expect_ty() + } +} + #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub enum ExistentialPredicate<'tcx> { diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 2438d1a16021b..73a8e18949de0 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -3,7 +3,7 @@ use crate::mir; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::sty::{ClosureSubsts, GeneratorSubsts}; +use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts}; use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt}; use rustc_hir::def_id::DefId; @@ -204,6 +204,14 @@ impl<'a, 'tcx> InternalSubsts<'tcx> { GeneratorSubsts { substs: self } } + /// Interpret these substitutions as the substitutions of an inline const. + /// Inline const substitutions have a particular structure controlled by the + /// compiler that encodes information like the inferred type; + /// see `ty::InlineConstSubsts` struct for more comments. + pub fn as_inline_const(&'tcx self) -> InlineConstSubsts<'tcx> { + InlineConstSubsts { substs: self } + } + /// Creates an `InternalSubsts` that maps each generic parameter to itself. pub fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { Self::for_item(tcx, def_id, |param, _| tcx.mk_param_from_def(param)) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 2c884813d2318..d5b78820411be 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -423,6 +423,14 @@ impl<'tcx> TyCtxt<'tcx> { matches!(self.def_kind(def_id), DefKind::Closure | DefKind::Generator) } + /// Returns `true` if `def_id` refers to a closure, generator or inline const. + pub fn is_closure_or_inline_const(self, def_id: DefId) -> bool { + matches!( + self.def_kind(def_id), + DefKind::Closure | DefKind::Generator | DefKind::InlineConst + ) + } + /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). pub fn is_trait(self, def_id: DefId) -> bool { self.def_kind(def_id) == DefKind::Trait @@ -440,16 +448,19 @@ impl<'tcx> TyCtxt<'tcx> { matches!(self.def_kind(def_id), DefKind::Ctor(..)) } - /// Given the def-ID of a fn or closure, returns the def-ID of - /// the innermost fn item that the closure is contained within. - /// This is a significant `DefId` because, when we do - /// type-checking, we type-check this fn item and all of its - /// (transitive) closures together. Therefore, when we fetch the + /// Given the `DefId`, returns the `DefId` of the innermost item that + /// has its own type-checking context or "inference enviornment". + /// + /// For example, a closure has its own `DefId`, but it is type-checked + /// with the containing item. Similarly, an inline const block has its + /// own `DefId` but it is type-checked together with the containing item. + /// + /// Therefore, when we fetch the /// `typeck` the closure, for example, we really wind up /// fetching the `typeck` the enclosing fn item. pub fn closure_base_def_id(self, def_id: DefId) -> DefId { let mut def_id = def_id; - while self.is_closure(def_id) { + while self.is_closure_or_inline_const(def_id) { def_id = self.parent(def_id).unwrap_or_else(|| { bug!("closure {:?} has no parent", def_id); }); diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 17296a95bc17e..b4005ccd1cc42 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -578,7 +578,7 @@ impl<'tcx> Cx<'tcx> { hir::ExprKind::ConstBlock(ref anon_const) => { let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); - let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id); + let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id); ExprKind::ConstBlock { value } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index cb74ae4df2ef8..ce80214c875fc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -544,7 +544,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let (lit, neg) = match expr.kind { hir::ExprKind::ConstBlock(ref anon_const) => { let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); - let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id); + let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id); if matches!(value.val, ConstKind::Param(_)) { let span = self.tcx.hir().span(anon_const.hir_id); self.errors.push(PatternError::ConstParamInPattern(span)); diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs index 5fc8e230d72a3..40905c1f8affc 100644 --- a/compiler/rustc_passes/src/region.rs +++ b/compiler/rustc_passes/src/region.rs @@ -334,9 +334,10 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h // properly, we can't miss any types. match expr.kind { - // Manually recurse over closures, because they are the only + // Manually recurse over closures and inline consts, because they are the only // case of nested bodies that share the parent environment. - hir::ExprKind::Closure(.., body, _, _) => { + hir::ExprKind::Closure(.., body, _, _) + | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => { let body = visitor.tcx.hir().body(body); visitor.visit_body(body); } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 2d0a4068fbbe3..c03ae17b17236 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -30,6 +30,7 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -323,7 +324,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(args) => self.check_expr_array(args, expected, expr), - ExprKind::ConstBlock(ref anon_const) => self.to_const(anon_const).ty, + ExprKind::ConstBlock(ref anon_const) => { + self.check_expr_const_block(anon_const, expected, expr) + } ExprKind::Repeat(element, ref count) => { self.check_expr_repeat(element, count, expected, expr) } @@ -1166,6 +1169,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_array(element_ty, args.len() as u64) } + fn check_expr_const_block( + &self, + anon_const: &'tcx hir::AnonConst, + expected: Expectation<'tcx>, + _expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let body = self.tcx.hir().body(anon_const.body); + + // Create a new function context. + let fcx = FnCtxt::new(self, self.param_env, body.value.hir_id); + crate::check::GatherLocalsVisitor::new(&fcx).visit_body(body); + + let ty = fcx.check_expr_with_expectation(&body.value, expected); + fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized); + fcx.write_ty(anon_const.hir_id, ty); + ty + } + fn check_expr_repeat( &self, element: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 5aa11cce25fb6..cbf33cf1b78a1 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -292,7 +292,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // String and byte-string literals result in types `&str` and `&[u8]` respectively. // All other literals result in non-reference types. // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`. - PatKind::Lit(lt) => match self.check_expr(lt).kind() { + // + // Call `resolve_vars_if_possible` here for inline const blocks. + PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_expr(lt)).kind() { ty::Ref(..) => AdjustMode::Pass, _ => AdjustMode::Peel, }, diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 86d4e4d2b115b..52faf5fbe9fb3 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -341,6 +341,29 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { self.visit_region_obligations(body_id.hir_id); } + fn visit_inline_const(&mut self, id: hir::HirId, body: &'tcx hir::Body<'tcx>) { + debug!("visit_inline_const(id={:?})", id); + + // Save state of current function. We will restore afterwards. + let old_body_id = self.body_id; + let old_body_owner = self.body_owner; + let env_snapshot = self.outlives_environment.push_snapshot_pre_closure(); + + let body_id = body.id(); + self.body_id = body_id.hir_id; + self.body_owner = self.tcx.hir().body_owner_def_id(body_id); + + self.outlives_environment.save_implied_bounds(body_id.hir_id); + + self.visit_body(body); + self.visit_region_obligations(body_id.hir_id); + + // Restore state from previous function. + self.outlives_environment.pop_snapshot_post_closure(env_snapshot); + self.body_id = old_body_id; + self.body_owner = old_body_owner; + } + fn visit_region_obligations(&mut self, hir_id: hir::HirId) { debug!("visit_region_obligations: hir_id={:?}", hir_id); @@ -460,6 +483,11 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { intravisit::walk_expr(self, expr); } + hir::ExprKind::ConstBlock(anon_const) => { + let body = self.tcx.hir().body(anon_const.body); + self.visit_inline_const(anon_const.hir_id, body); + } + _ => intravisit::walk_expr(self, expr), } } diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index d951df94dcf50..fdc8b6b5e6451 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -282,6 +282,12 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { hir::ExprKind::Field(..) => { self.visit_field_id(e.hir_id); } + hir::ExprKind::ConstBlock(anon_const) => { + self.visit_node_id(e.span, anon_const.hir_id); + + let body = self.tcx().hir().body(anon_const.body); + self.visit_body(body); + } _ => {} } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 18e8ed394e814..11666fcc2d84f 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1494,7 +1494,9 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { { Some(parent_def_id.to_def_id()) } - + Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => { + Some(tcx.closure_base_def_id(def_id)) + } _ => None, } } @@ -1692,6 +1694,24 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { })); } + // provide junk type parameter defs for const blocks. + if let Node::AnonConst(_) = node { + let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node { + params.push(ty::GenericParamDef { + index: type_start, + name: Symbol::intern(""), + def_id, + pure_wrt_drop: false, + kind: ty::GenericParamDefKind::Type { + has_default: false, + object_lifetime_default: rl::Set1::Empty, + synthetic: None, + }, + }); + } + } + let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); ty::Generics { diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index a6ea8abdf3fa6..04a68250ced0c 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -494,7 +494,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. }) if anon_const.hir_id == hir_id => { - tcx.typeck(def_id).node_type(anon_const.hir_id) + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + substs.as_inline_const().ty() } Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) From 4acef9ac19133e7cd3a0648ab6395c7c4eb3031f Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 3 Oct 2021 16:03:34 +0100 Subject: [PATCH 07/17] Add unit test for issue 78132 --- src/test/ui/inline-const/const-expr-inference.rs | 12 ++++++++++++ .../ui/inline-const/const-match-pat-inference.rs | 12 ++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/test/ui/inline-const/const-expr-inference.rs create mode 100644 src/test/ui/inline-const/const-match-pat-inference.rs diff --git a/src/test/ui/inline-const/const-expr-inference.rs b/src/test/ui/inline-const/const-expr-inference.rs new file mode 100644 index 0000000000000..6aa2a2f3367fc --- /dev/null +++ b/src/test/ui/inline-const/const-expr-inference.rs @@ -0,0 +1,12 @@ +// check-pass + +#![feature(inline_const)] +#![allow(incomplete_features)] + +pub fn todo() -> T { + const { todo!() } +} + +fn main() { + let _: usize = const { 0 }; +} diff --git a/src/test/ui/inline-const/const-match-pat-inference.rs b/src/test/ui/inline-const/const-match-pat-inference.rs new file mode 100644 index 0000000000000..61188ed5d47bd --- /dev/null +++ b/src/test/ui/inline-const/const-match-pat-inference.rs @@ -0,0 +1,12 @@ +// check-pass + +#![feature(inline_const)] +#![allow(incomplete_features)] + +fn main() { + match 1u64 { + 0 => (), + const { 0 + 1 } => (), + const { 2 - 1 } ..= const { u64::MAX } => (), + } +} From 1d32b201707ce6327c0ab322c4e06d8e0367f563 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 3 Oct 2021 16:04:30 +0100 Subject: [PATCH 08/17] Add unit test for issue 78174 --- .../ui/inline-const/const-expr-lifetime.rs | 36 +++++++++++++++++++ .../inline-const/const-match-pat-lifetime.rs | 36 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/test/ui/inline-const/const-expr-lifetime.rs create mode 100644 src/test/ui/inline-const/const-match-pat-lifetime.rs diff --git a/src/test/ui/inline-const/const-expr-lifetime.rs b/src/test/ui/inline-const/const-expr-lifetime.rs new file mode 100644 index 0000000000000..f622f2cbddf48 --- /dev/null +++ b/src/test/ui/inline-const/const-expr-lifetime.rs @@ -0,0 +1,36 @@ +// run-pass + +#![allow(incomplete_features)] +#![feature(const_mut_refs)] +#![feature(inline_const)] + +use std::marker::PhantomData; + +// rust-lang/rust#78174: ICE: "cannot convert ReErased to a region vid" +fn issue_78174() { + let foo = const { "foo" }; + assert_eq!(foo, "foo"); +} + +pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>); + +impl<'a, T: ?Sized> InvariantRef<'a, T> { + pub const fn new(r: &'a T) -> Self { + InvariantRef(r, PhantomData) + } +} + +fn get_invariant_ref<'a>() -> InvariantRef<'a, ()> { + const { InvariantRef::<'a, ()>::new(&()) } +} + +fn get_invariant_ref2<'a>() -> InvariantRef<'a, ()> { + // Try some type inference + const { InvariantRef::new(&()) } +} + +fn main() { + issue_78174(); + get_invariant_ref(); + get_invariant_ref2(); +} diff --git a/src/test/ui/inline-const/const-match-pat-lifetime.rs b/src/test/ui/inline-const/const-match-pat-lifetime.rs new file mode 100644 index 0000000000000..3d986f0d9ee08 --- /dev/null +++ b/src/test/ui/inline-const/const-match-pat-lifetime.rs @@ -0,0 +1,36 @@ +// run-pass + +#![allow(incomplete_features)] +#![feature(const_mut_refs)] +#![feature(inline_const)] + +use std::marker::PhantomData; + +// rust-lang/rust#78174: ICE: "cannot convert ReErased to a region vid" +fn issue_78174() { + match "foo" { + const { concat!("fo", "o") } => (), + _ => unreachable!(), + } +} + +#[derive(PartialEq, Eq)] +pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>); + +impl<'a, T: ?Sized> InvariantRef<'a, T> { + pub const fn new(r: &'a T) -> Self { + InvariantRef(r, PhantomData) + } +} + +fn match_invariant_ref<'a>() { + match const { InvariantRef::<'a, _>::new(&()) } { + const { InvariantRef::<'a, ()>::new(&()) } => { + } + } +} + +fn main() { + issue_78174(); + match_invariant_ref(); +} From ff055e2135574a0b795c0bc03144a89b54351af7 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 3 Oct 2021 23:14:33 +0100 Subject: [PATCH 09/17] Ensure closure requirements are proven for inline const --- compiler/rustc_borrowck/src/type_check/mod.rs | 84 +++++++++++++++++-- .../inline-const/const-expr-lifetime-err.rs | 30 +++++++ .../const-expr-lifetime-err.stderr | 18 ++++ 3 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/inline-const/const-expr-lifetime-err.rs create mode 100644 src/test/ui/inline-const/const-expr-lifetime-err.stderr diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 9e068ef716504..890f1235a9d43 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -10,6 +10,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::vec_map::VecMap; use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::lang_items::LangItem; use rustc_index::vec::{Idx, IndexVec}; @@ -1532,6 +1533,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { + self.check_operand(discr, term_location); + let discr_ty = discr.ty(body, tcx); if let Err(terr) = self.sub_types( discr_ty, @@ -1554,6 +1557,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // FIXME: check the values } TerminatorKind::Call { ref func, ref args, ref destination, from_hir_call, .. } => { + self.check_operand(func, term_location); + for arg in args { + self.check_operand(arg, term_location); + } + let func_ty = func.ty(body, tcx); debug!("check_terminator: call, func_ty={:?}", func_ty); let sig = match func_ty.kind() { @@ -1598,6 +1606,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_call_inputs(body, term, &sig, args, term_location, from_hir_call); } TerminatorKind::Assert { ref cond, ref msg, .. } => { + self.check_operand(cond, term_location); + let cond_ty = cond.ty(body, tcx); if cond_ty != tcx.types.bool { span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); @@ -1613,6 +1623,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } TerminatorKind::Yield { ref value, .. } => { + self.check_operand(value, term_location); + let value_ty = value.ty(body, tcx); match body.yield_ty() { None => span_mirbug!(self, term, "yield in non-generator"), @@ -1941,15 +1953,51 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } + fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) { + if let Operand::Constant(constant) = op { + let maybe_uneval = match constant.literal { + ConstantKind::Ty(ct) => match ct.val { + ty::ConstKind::Unevaluated(uv) => Some(uv), + _ => None, + }, + _ => None, + }; + if let Some(uv) = maybe_uneval { + if uv.promoted.is_none() { + let tcx = self.tcx(); + let def_id = uv.def.def_id_for_type_of(); + if tcx.def_kind(def_id) == DefKind::InlineConst { + let predicates = self.prove_closure_bounds( + tcx, + def_id.expect_local(), + uv.substs(tcx), + location, + ); + self.normalize_and_prove_instantiated_predicates( + def_id, + predicates, + location.to_locations(), + ); + } + } + } + } + } + fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { let tcx = self.tcx(); match rvalue { Rvalue::Aggregate(ak, ops) => { + for op in ops { + self.check_operand(op, location); + } self.check_aggregate_rvalue(&body, rvalue, ak, ops, location) } Rvalue::Repeat(operand, len) => { + self.check_operand(operand, location); + // If the length cannot be evaluated we must assume that the length can be larger // than 1. // If the length is larger than 1, the repeat expression will need to copy the @@ -2000,7 +2048,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - Rvalue::NullaryOp(_, ty) | Rvalue::ShallowInitBox(_, ty) => { + Rvalue::NullaryOp(_, ty) => { + let trait_ref = ty::TraitRef { + def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), + substs: tcx.mk_substs_trait(ty, &[]), + }; + + self.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::SizedBound, + ); + } + + Rvalue::ShallowInitBox(operand, ty) => { + self.check_operand(operand, location); + let trait_ref = ty::TraitRef { def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), substs: tcx.mk_substs_trait(ty, &[]), @@ -2014,6 +2077,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } Rvalue::Cast(cast_kind, op, ty) => { + self.check_operand(op, location); + match cast_kind { CastKind::Pointer(PointerCast::ReifyFnPointer) => { let fn_sig = op.ty(body, tcx).fn_sig(tcx); @@ -2260,6 +2325,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, box (left, right), ) => { + self.check_operand(left, location); + self.check_operand(right, location); + let ty_left = left.ty(body, tcx); match ty_left.kind() { // Types with regions are comparable if they have a common super-type. @@ -2310,13 +2378,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } + Rvalue::Use(operand) | Rvalue::UnaryOp(_, operand) => { + self.check_operand(operand, location); + } + + Rvalue::BinaryOp(_, box (left, right)) + | Rvalue::CheckedBinaryOp(_, box (left, right)) => { + self.check_operand(left, location); + self.check_operand(right, location); + } + Rvalue::AddressOf(..) | Rvalue::ThreadLocalRef(..) - | Rvalue::Use(..) | Rvalue::Len(..) - | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) - | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) => {} } } diff --git a/src/test/ui/inline-const/const-expr-lifetime-err.rs b/src/test/ui/inline-const/const-expr-lifetime-err.rs new file mode 100644 index 0000000000000..e56cbc94038a0 --- /dev/null +++ b/src/test/ui/inline-const/const-expr-lifetime-err.rs @@ -0,0 +1,30 @@ +#![allow(incomplete_features)] +#![feature(const_mut_refs)] +#![feature(inline_const)] + +use std::marker::PhantomData; + +#[derive(PartialEq, Eq)] +pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>); + +impl<'a, T: ?Sized> InvariantRef<'a, T> { + pub const fn new(r: &'a T) -> Self { + InvariantRef(r, PhantomData) + } +} + +impl<'a> InvariantRef<'a, ()> { + pub const NEW: Self = InvariantRef::new(&()); +} + +fn equate(x: T, y: T){} + +fn foo<'a>() { + let y = (); + equate(InvariantRef::new(&y), const { InvariantRef::<'a>::NEW }); + //~^ ERROR `y` does not live long enough [E0597] +} + +fn main() { + foo(); +} diff --git a/src/test/ui/inline-const/const-expr-lifetime-err.stderr b/src/test/ui/inline-const/const-expr-lifetime-err.stderr new file mode 100644 index 0000000000000..30ecd338a856d --- /dev/null +++ b/src/test/ui/inline-const/const-expr-lifetime-err.stderr @@ -0,0 +1,18 @@ +error[E0597]: `y` does not live long enough + --> $DIR/const-expr-lifetime-err.rs:24:30 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +LL | let y = (); +LL | equate(InvariantRef::new(&y), const { InvariantRef::<'a>::NEW }); + | ------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `y` is borrowed for `'a` +LL | +LL | } + | - `y` dropped here while still borrowed + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. From 4060ed7eff2dfb38ee8f9e3d2806f9d82c2cfc65 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 3 Oct 2021 16:05:55 +0100 Subject: [PATCH 10/17] Add a FIXME note about what's missing --- .../const-match-pat-lifetime-err.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/test/ui/inline-const/const-match-pat-lifetime-err.rs diff --git a/src/test/ui/inline-const/const-match-pat-lifetime-err.rs b/src/test/ui/inline-const/const-match-pat-lifetime-err.rs new file mode 100644 index 0000000000000..bc5aa24894427 --- /dev/null +++ b/src/test/ui/inline-const/const-match-pat-lifetime-err.rs @@ -0,0 +1,34 @@ +// ignore-test + +#![allow(incomplete_features)] +#![feature(const_mut_refs)] +#![feature(inline_const)] + +use std::marker::PhantomData; + +#[derive(PartialEq, Eq)] +pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>); + +impl<'a, T: ?Sized> InvariantRef<'a, T> { + pub const fn new(r: &'a T) -> Self { + InvariantRef(r, PhantomData) + } +} + +impl<'a> InvariantRef<'a, ()> { + pub const NEW: Self = InvariantRef::new(&()); +} + +fn match_invariant_ref<'a>() { + let y = (); + match InvariantRef::new(&y) { + //~^ ERROR `y` does not live long enough [E0597] + // FIXME(nbdd0121): This should give the same error as `InvariantRef::<'a>::NEW` (without + // const block) + const { InvariantRef::<'a>::NEW } => (), + } +} + +fn main() { + match_invariant_ref(); +} From d0f59f6d6575e04d3e4d08e919ce01e29bc9e41e Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 24 Oct 2021 00:26:40 +0100 Subject: [PATCH 11/17] Fix closures within inline const --- compiler/rustc_typeck/src/check/upvar.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 774d8078e52ca..5f5d308a3329b 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -148,10 +148,17 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Closure(cc, _, body_id, _, _) = expr.kind { - let body = self.fcx.tcx.hir().body(body_id); - self.visit_body(body); - self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, cc); + match expr.kind { + hir::ExprKind::Closure(cc, _, body_id, _, _) => { + let body = self.fcx.tcx.hir().body(body_id); + self.visit_body(body); + self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, cc); + } + hir::ExprKind::ConstBlock(anon_const) => { + let body = self.fcx.tcx.hir().body(anon_const.body); + self.visit_body(body); + } + _ => {} } intravisit::walk_expr(self, expr); From c4103d438fa922edbe5c40085f40b96656c6b18a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 6 Nov 2021 20:01:35 +0000 Subject: [PATCH 12/17] Rename functions reflect that inline const is also "typeck_child" --- .../src/diagnostics/conflict_errors.rs | 2 +- compiler/rustc_borrowck/src/nll.rs | 2 +- .../rustc_borrowck/src/region_infer/mod.rs | 5 +-- compiler/rustc_borrowck/src/type_check/mod.rs | 15 ++----- .../rustc_borrowck/src/universal_regions.rs | 40 +++++++++---------- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- .../rustc_infer/src/infer/outlives/env.rs | 8 ++-- compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_middle/src/ty/consts.rs | 10 ++--- compiler/rustc_middle/src/ty/util.rs | 9 +++-- .../rustc_monomorphize/src/polymorphize.rs | 2 +- compiler/rustc_passes/src/region.rs | 6 +-- .../src/traits/error_reporting/suggestions.rs | 2 +- compiler/rustc_typeck/src/check/closure.rs | 2 +- compiler/rustc_typeck/src/check/mod.rs | 12 +++--- compiler/rustc_typeck/src/check/regionck.rs | 8 ++-- compiler/rustc_typeck/src/collect.rs | 4 +- src/librustdoc/core.rs | 6 +-- 18 files changed, 64 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 439c728798d3a..46a3c0fa10152 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -408,7 +408,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let param = generics.type_param(¶m_ty, tcx); if let Some(generics) = tcx .hir() - .get_generics(tcx.closure_base_def_id(self.mir_def_id().to_def_id())) + .get_generics(tcx.typeck_root_def_id(self.mir_def_id().to_def_id())) { suggest_constraining_type_param( tcx, diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index e5924f9d08478..6ffab16577908 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -376,7 +376,7 @@ pub(super) fn dump_annotation<'a, 'tcx>( errors_buffer: &mut Vec, ) { let tcx = infcx.tcx; - let base_def_id = tcx.closure_base_def_id(body.source.def_id()); + let base_def_id = tcx.typeck_root_def_id(body.source.def_id()); if !tcx.has_attr(base_def_id, sym::rustc_regions) { return; } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 053e5c33313cc..b39a28f79aadd 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -569,8 +569,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // to store those. Otherwise, we'll pass in `None` to the // functions below, which will trigger them to report errors // eagerly. - let mut outlives_requirements = - infcx.tcx.is_closure_or_inline_const(mir_def_id).then(Vec::new); + let mut outlives_requirements = infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new); self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer); @@ -2230,7 +2229,7 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx tcx, closure_substs, self.num_external_vids, - tcx.closure_base_def_id(closure_def_id), + tcx.typeck_root_def_id(closure_def_id), ); debug!("apply_requirements: closure_mapping={:?}", closure_mapping); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 890f1235a9d43..82f68605b281f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1344,18 +1344,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // though. let category = match place.as_local() { Some(RETURN_PLACE) => { - if let BorrowCheckContext { - universal_regions: - UniversalRegions { - defining_ty: - DefiningTy::Const(def_id, _) - | DefiningTy::InlineConst(def_id, _), - .. - }, - .. - } = self.borrowck_context - { - if tcx.is_static(*def_id) { + let defining_ty = &self.borrowck_context.universal_regions.defining_ty; + if defining_ty.is_const() { + if tcx.is_static(defining_ty.def_id()) { ConstraintCategory::UseAsStatic } else { ConstraintCategory::UseAsConst diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index ab8725834376c..b986df403f9f3 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -247,7 +247,7 @@ impl<'tcx> UniversalRegions<'tcx> { tcx: TyCtxt<'tcx>, closure_substs: SubstsRef<'tcx>, expected_num_vars: usize, - closure_base_def_id: DefId, + typeck_root_def_id: DefId, ) -> IndexVec> { let mut region_mapping = IndexVec::with_capacity(expected_num_vars); region_mapping.push(tcx.lifetimes.re_static); @@ -255,7 +255,7 @@ impl<'tcx> UniversalRegions<'tcx> { region_mapping.push(fr); }); - for_each_late_bound_region_defined_on(tcx, closure_base_def_id, |r| { + for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| { region_mapping.push(r); }); @@ -349,8 +349,8 @@ impl<'tcx> UniversalRegions<'tcx> { // tests, and the resulting print-outs include def-ids // and other things that are not stable across tests! // So we just include the region-vid. Annoying. - let closure_base_def_id = tcx.closure_base_def_id(def_id); - for_each_late_bound_region_defined_on(tcx, closure_base_def_id, |r| { + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| { err.note(&format!("late-bound region is {:?}", self.to_region_vid(r),)); }); } @@ -364,8 +364,8 @@ impl<'tcx> UniversalRegions<'tcx> { // FIXME: As above, we'd like to print out the region // `r` but doing so is not stable across architectures // and so forth. - let closure_base_def_id = tcx.closure_base_def_id(def_id); - for_each_late_bound_region_defined_on(tcx, closure_base_def_id, |r| { + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| { err.note(&format!("late-bound region is {:?}", self.to_region_vid(r),)); }); } @@ -422,7 +422,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let mut indices = self.compute_indices(fr_static, defining_ty); debug!("build: indices={:?}", indices); - let closure_base_def_id = self.infcx.tcx.closure_base_def_id(self.mir_def.did.to_def_id()); + let typeck_root_def_id = self.infcx.tcx.typeck_root_def_id(self.mir_def.did.to_def_id()); // If this is a closure or generator, then the late-bound regions from the enclosing // function are actually external regions to us. For example, here, 'a is not local @@ -430,7 +430,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { // fn foo<'a>() { // let c = || { let x: &'a u32 = ...; } // } - if self.mir_def.did.to_def_id() != closure_base_def_id { + if self.mir_def.did.to_def_id() != typeck_root_def_id { self.infcx .replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices) } @@ -448,7 +448,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { ); // Converse of above, if this is a function then the late-bound regions declared on its // signature are local to the fn. - if self.mir_def.did.to_def_id() == closure_base_def_id { + if self.mir_def.did.to_def_id() == typeck_root_def_id { self.infcx .replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices); } @@ -513,12 +513,12 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { /// see `DefiningTy` for details. fn defining_ty(&self) -> DefiningTy<'tcx> { let tcx = self.infcx.tcx; - let closure_base_def_id = tcx.closure_base_def_id(self.mir_def.did.to_def_id()); + let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.did.to_def_id()); match tcx.hir().body_owner_kind(self.mir_hir_id) { BodyOwnerKind::Closure | BodyOwnerKind::Fn => { - let defining_ty = if self.mir_def.did.to_def_id() == closure_base_def_id { - tcx.type_of(closure_base_def_id) + let defining_ty = if self.mir_def.did.to_def_id() == typeck_root_def_id { + tcx.type_of(typeck_root_def_id) } else { let tables = tcx.typeck(self.mir_def.did); tables.node_type(self.mir_hir_id) @@ -545,8 +545,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { } BodyOwnerKind::Const | BodyOwnerKind::Static(..) => { - let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id); - if self.mir_def.did.to_def_id() == closure_base_def_id { + let identity_substs = InternalSubsts::identity_for_item(tcx, typeck_root_def_id); + if self.mir_def.did.to_def_id() == typeck_root_def_id { let substs = self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs); DefiningTy::Const(self.mir_def.did.to_def_id(), substs) @@ -574,19 +574,19 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { defining_ty: DefiningTy<'tcx>, ) -> UniversalRegionIndices<'tcx> { let tcx = self.infcx.tcx; - let closure_base_def_id = tcx.closure_base_def_id(self.mir_def.did.to_def_id()); - let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id); + let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.did.to_def_id()); + let identity_substs = InternalSubsts::identity_for_item(tcx, typeck_root_def_id); let fr_substs = match defining_ty { DefiningTy::Closure(_, ref substs) | DefiningTy::Generator(_, ref substs, _) | DefiningTy::InlineConst(_, ref substs) => { // In the case of closures, we rely on the fact that // the first N elements in the ClosureSubsts are - // inherited from the `closure_base_def_id`. + // inherited from the `typeck_root_def_id`. // Therefore, when we zip together (below) with // `identity_substs`, we will get only those regions // that correspond to early-bound regions declared on - // the `closure_base_def_id`. + // the `typeck_root_def_id`. assert!(substs.len() >= identity_substs.len()); assert_eq!(substs.regions().count(), identity_substs.regions().count()); substs @@ -765,8 +765,8 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { indices: &mut UniversalRegionIndices<'tcx>, ) { debug!("replace_late_bound_regions_with_nll_infer_vars(mir_def_id={:?})", mir_def_id); - let closure_base_def_id = self.tcx.closure_base_def_id(mir_def_id.to_def_id()); - for_each_late_bound_region_defined_on(self.tcx, closure_base_def_id, |r| { + let typeck_root_def_id = self.tcx.typeck_root_def_id(mir_def_id.to_def_id()); + for_each_late_bound_region_defined_on(self.tcx, typeck_root_def_id, |r| { debug!("replace_late_bound_regions_with_nll_infer_vars: r={:?}", r); if !indices.indices.contains_key(&r) { let region_vid = self.next_nll_region_var(FR); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 1f1bd73c7d035..2a6bf7d9b1a4d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -322,7 +322,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { type_names::push_item_name(self.tcx(), def_id, false, &mut name); // Find the enclosing function, in case this is a closure. - let enclosing_fn_def_id = self.tcx().closure_base_def_id(def_id); + let enclosing_fn_def_id = self.tcx().typeck_root_def_id(def_id); // Get_template_parameters() will append a `<...>` clause to the function // name if necessary. diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 9e04773c5fa20..3947282aa6217 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -99,7 +99,7 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { /// function. We can then add implied bounds and the like from the /// closure arguments into the environment -- these should only /// apply in the closure body, so once we exit, we invoke - /// `pop_snapshot_post_closure` to remove them. + /// `pop_snapshot_post_typeck_child` to remove them. /// /// Example: /// @@ -129,12 +129,12 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { /// seems like it'd be readily fixed if we wanted. There are /// similar leaks around givens that seem equally suspicious, to /// be honest. --nmatsakis - pub fn push_snapshot_pre_closure(&self) -> usize { + pub fn push_snapshot_pre_typeck_child(&self) -> usize { self.region_bound_pairs_accum.len() } - /// See `push_snapshot_pre_closure`. - pub fn pop_snapshot_post_closure(&mut self, len: usize) { + /// See `push_snapshot_pre_typeck_child`. + pub fn pop_snapshot_post_typeck_child(&mut self, len: usize) { self.region_bound_pairs_accum.truncate(len); } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b0952d346766c..a9f94b74c5efb 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -797,7 +797,7 @@ rustc_queries! { /// additional requirements that the closure's creator must verify. query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> { desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if(tcx) { tcx.is_closure_or_inline_const(key.to_def_id()) } + cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) } } query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> { desc { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 080ef6cb82a0f..27e22ccac02a7 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -56,7 +56,7 @@ impl<'tcx> Const<'tcx> { let ty = tcx.type_of(def.def_id_for_type_of()); - match Self::try_eval_body_expr(tcx, ty, expr) { + match Self::try_eval_lit_or_param(tcx, ty, expr) { Some(v) => v, None => tcx.mk_const(ty::Const { val: ty::ConstKind::Unevaluated(ty::Unevaluated { @@ -69,7 +69,7 @@ impl<'tcx> Const<'tcx> { } } - fn try_eval_body_expr( + fn try_eval_lit_or_param( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>, @@ -141,12 +141,12 @@ impl<'tcx> Const<'tcx> { let ty = tcx.typeck(def_id).node_type(hir_id); - let ret = match Self::try_eval_body_expr(tcx, ty, expr) { + let ret = match Self::try_eval_lit_or_param(tcx, ty, expr) { Some(v) => v, None => { - let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()); + let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()); let parent_substs = - tcx.erase_regions(InternalSubsts::identity_for_item(tcx, outer_def_id)); + tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id)); let substs = InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty }) .substs; diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index d5b78820411be..6b287445ff032 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -423,8 +423,9 @@ impl<'tcx> TyCtxt<'tcx> { matches!(self.def_kind(def_id), DefKind::Closure | DefKind::Generator) } - /// Returns `true` if `def_id` refers to a closure, generator or inline const. - pub fn is_closure_or_inline_const(self, def_id: DefId) -> bool { + /// Returns `true` if `def_id` refers to a definition that does not have its own + /// type-checking context, i.e. closure, generator or inline const. + pub fn is_typeck_child(self, def_id: DefId) -> bool { matches!( self.def_kind(def_id), DefKind::Closure | DefKind::Generator | DefKind::InlineConst @@ -458,9 +459,9 @@ impl<'tcx> TyCtxt<'tcx> { /// Therefore, when we fetch the /// `typeck` the closure, for example, we really wind up /// fetching the `typeck` the enclosing fn item. - pub fn closure_base_def_id(self, def_id: DefId) -> DefId { + pub fn typeck_root_def_id(self, def_id: DefId) -> DefId { let mut def_id = def_id; - while self.is_closure_or_inline_const(def_id) { + while self.is_typeck_child(def_id) { def_id = self.parent(def_id).unwrap_or_else(|| { bug!("closure {:?} has no parent", def_id); }); diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index ba62a6eea10ea..595080619da6f 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -196,7 +196,7 @@ fn emit_unused_generic_params_error<'tcx>( generics: &'tcx ty::Generics, unused_parameters: &FiniteBitSet, ) { - let base_def_id = tcx.closure_base_def_id(def_id); + let base_def_id = tcx.typeck_root_def_id(def_id); if !tcx.get_attrs(base_def_id).iter().any(|a| a.has_name(sym::rustc_polymorphize_error)) { return; } diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs index 40905c1f8affc..6a8feb041da19 100644 --- a/compiler/rustc_passes/src/region.rs +++ b/compiler/rustc_passes/src/region.rs @@ -818,9 +818,9 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { } fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { - let closure_base_def_id = tcx.closure_base_def_id(def_id); - if closure_base_def_id != def_id { - return tcx.region_scope_tree(closure_base_def_id); + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + if typeck_root_def_id != def_id { + return tcx.region_scope_tree(typeck_root_def_id); } let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 2689e2134fc6b..b21936a00b04f 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1474,7 +1474,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let span = self.tcx.def_span(generator_did); let in_progress_typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()); - let generator_did_root = self.tcx.closure_base_def_id(generator_did); + let generator_did_root = self.tcx.typeck_root_def_id(generator_did); debug!( "maybe_note_obligation_cause_for_async_await: generator_did={:?} \ generator_did_root={:?} in_progress_typeck_results.hir_owner={:?} span={:?}", diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 410ac24b1f19c..f7accbb430caa 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -92,7 +92,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let parent_substs = InternalSubsts::identity_for_item( self.tcx, - self.tcx.closure_base_def_id(expr_def_id.to_def_id()), + self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), ); let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin { diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 7450b4a4ef1c3..d19e99606bcd8 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -297,9 +297,9 @@ fn primary_body_of( fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { // Closures' typeck results come from their outermost function, // as they are part of the same "inference environment". - let outer_def_id = tcx.closure_base_def_id(def_id); - if outer_def_id != def_id { - return tcx.has_typeck_results(outer_def_id); + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + if typeck_root_def_id != def_id { + return tcx.has_typeck_results(typeck_root_def_id); } if let Some(def_id) = def_id.as_local() { @@ -348,9 +348,9 @@ fn typeck_with_fallback<'tcx>( ) -> &'tcx ty::TypeckResults<'tcx> { // Closures' typeck results come from their outermost function, // as they are part of the same "inference environment". - let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local(); - if outer_def_id != def_id { - return tcx.typeck(outer_def_id); + let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); + if typeck_root_def_id != def_id { + return tcx.typeck(typeck_root_def_id); } let id = tcx.hir().local_def_id_to_hir_id(def_id); diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 52faf5fbe9fb3..d2d8b14dd9695 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -347,7 +347,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { // Save state of current function. We will restore afterwards. let old_body_id = self.body_id; let old_body_owner = self.body_owner; - let env_snapshot = self.outlives_environment.push_snapshot_pre_closure(); + let env_snapshot = self.outlives_environment.push_snapshot_pre_typeck_child(); let body_id = body.id(); self.body_id = body_id.hir_id; @@ -359,7 +359,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { self.visit_region_obligations(body_id.hir_id); // Restore state from previous function. - self.outlives_environment.pop_snapshot_post_closure(env_snapshot); + self.outlives_environment.pop_snapshot_post_typeck_child(env_snapshot); self.body_id = old_body_id; self.body_owner = old_body_owner; } @@ -429,13 +429,13 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { // `visit_fn_body`. We will restore afterwards. let old_body_id = self.body_id; let old_body_owner = self.body_owner; - let env_snapshot = self.outlives_environment.push_snapshot_pre_closure(); + let env_snapshot = self.outlives_environment.push_snapshot_pre_typeck_child(); let body = self.tcx.hir().body(body_id); self.visit_fn_body(hir_id, body, span); // Restore state from previous function. - self.outlives_environment.pop_snapshot_post_closure(env_snapshot); + self.outlives_environment.pop_snapshot_post_typeck_child(env_snapshot); self.body_id = old_body_id; self.body_owner = old_body_owner; } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 11666fcc2d84f..2274db76c05fb 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1495,14 +1495,14 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { Some(parent_def_id.to_def_id()) } Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => { - Some(tcx.closure_base_def_id(def_id)) + Some(tcx.typeck_root_def_id(def_id)) } _ => None, } } } Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { - Some(tcx.closure_base_def_id(def_id)) + Some(tcx.typeck_root_def_id(def_id)) } Node::Item(item) => match item.kind { ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b7251e8f57151..3ed711c43e16d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -281,9 +281,9 @@ crate fn create_config( // Closures' tables come from their outermost function, // as they are part of the same "inference environment". // This avoids emitting errors for the parent twice (see similar code in `typeck_with_fallback`) - let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local(); - if outer_def_id != def_id { - return tcx.typeck(outer_def_id); + let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); + if typeck_root_def_id != def_id { + return tcx.typeck(typeck_root_def_id); } let hir = tcx.hir(); From 5bf3a5dfc72e68b4a631301b48b30cf913b8cfee Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 8 Nov 2021 04:16:34 +0000 Subject: [PATCH 13/17] Make `compiler-docs` only control the default instead of being a hard off-switch This also fixes `x doc src/tools/clippy` when compiler-docs is disabled. --- src/bootstrap/dist.rs | 6 ++---- src/bootstrap/doc.rs | 15 +++------------ 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index d4875cfe1b066..a799732adde9d 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -96,7 +96,8 @@ impl Step for RustcDocs { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/librustc") + let builder = run.builder; + run.path("rustc-docs").default_condition(builder.config.compiler_docs) } fn make_run(run: RunConfig<'_>) { @@ -106,9 +107,6 @@ impl Step for RustcDocs { /// Builds the `rustc-docs` installer component. fn run(self, builder: &Builder<'_>) -> Option { let host = self.host; - if !builder.config.compiler_docs { - return None; - } builder.default_doc(&[]); let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple); diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 2804e7119fbc1..f0f31c447bda4 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -529,7 +529,7 @@ impl Step for Rustc { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; - run.krate("rustc-main").path("compiler").default_condition(builder.config.docs) + run.krate("rustc-main").path("compiler").default_condition(builder.config.compiler_docs) } fn make_run(run: RunConfig<'_>) { @@ -560,11 +560,6 @@ impl Step for Rustc { }) .collect::>(); - if !builder.config.compiler_docs && !builder.was_invoked_explicitly::() { - builder.info("\tskipping - compiler/librustdoc docs disabled"); - return; - } - // This is the intended out directory for compiler documentation. let out = builder.compiler_doc_out(target); t!(fs::create_dir_all(&out)); @@ -674,7 +669,8 @@ macro_rules! tool_doc { const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.krate($should_run) + let builder = run.builder; + run.krate($should_run).default_condition(builder.config.compiler_docs) } fn make_run(run: RunConfig<'_>) { @@ -705,11 +701,6 @@ macro_rules! tool_doc { let compiler = builder.compiler(stage, builder.config.build); - if !builder.config.compiler_docs && !builder.was_invoked_explicitly::() { - builder.info("\tskipping - compiler/tool docs disabled"); - return; - } - // Build rustc docs so that we generate relative links. builder.ensure(Rustc { stage, target }); From 6192246e398b405c01d9d3ee2ec759946e5677bd Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 8 Nov 2021 04:45:15 +0000 Subject: [PATCH 14/17] x.py: remove fixme by deleting code As far as I can tell, this parameter was never used, so just delete it as unnecessary. --- src/bootstrap/tool.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index af6f4bb0e5fcb..1317c3f983975 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -286,7 +286,6 @@ macro_rules! bootstrap_tool { $name:ident, $path:expr, $tool_name:expr $(,is_external_tool = $external:expr)* $(,is_unstable_tool = $unstable:expr)* - $(,features = $features:expr)* ; )+) => { #[derive(Copy, PartialEq, Eq, Clone)] @@ -349,12 +348,7 @@ macro_rules! bootstrap_tool { } else { SourceType::InTree }, - extra_features: { - // FIXME(#60643): avoid this lint by using `_` - let mut _tmp = Vec::new(); - $(_tmp.extend($features);)* - _tmp - }, + extra_features: vec![], }).expect("expected to build -- essential tool") } } From fefe1e9192696c07f1655ed2726c4e114b70b096 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 8 Nov 2021 16:59:36 +0100 Subject: [PATCH 15/17] Record more artifact sizes during self-profiling. --- compiler/rustc_codegen_llvm/src/back/write.rs | 46 +++++++++++++++++++ compiler/rustc_codegen_ssa/src/back/link.rs | 13 ++++++ compiler/rustc_metadata/src/rmeta/encoder.rs | 3 ++ .../src/partitioning/mod.rs | 11 +++++ 4 files changed, 73 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 82c3c2006eb14..4abda33b2aa89 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -17,6 +17,7 @@ use rustc_codegen_ssa::back::write::{ }; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::small_c_str::SmallCStr; use rustc_errors::{FatalError, Handler, Level}; use rustc_fs_util::{link_or_copy, path_to_c_string}; @@ -53,6 +54,7 @@ pub fn write_output_file( output: &Path, dwo_output: Option<&Path>, file_type: llvm::FileType, + self_profiler_ref: &SelfProfilerRef, ) -> Result<(), FatalError> { unsafe { let output_c = path_to_c_string(output); @@ -76,6 +78,19 @@ pub fn write_output_file( file_type, ) }; + + // Record artifact sizes for self-profiling + if result == llvm::LLVMRustResult::Success { + let artifact_kind = match file_type { + llvm::FileType::ObjectFile => "object_file", + llvm::FileType::AssemblyFile => "assembly_file", + }; + record_artifact_size(self_profiler_ref, artifact_kind, output); + if let Some(dwo_file) = dwo_output { + record_artifact_size(self_profiler_ref, "dwo_file", dwo_file); + } + } + result.into_result().map_err(|()| { let msg = format!("could not write output to {}", output.display()); llvm_err(handler, &msg) @@ -752,6 +767,14 @@ pub(crate) unsafe fn codegen( let thin = ThinBuffer::new(llmod); let data = thin.data(); + if let Some(bitcode_filename) = bc_out.file_name() { + cgcx.prof.artifact_size( + "llvm_bitcode", + bitcode_filename.to_string_lossy(), + data.len() as u64, + ); + } + if config.emit_bc || config.emit_obj == EmitObj::Bitcode { let _timer = cgcx.prof.generic_activity_with_arg( "LLVM_module_codegen_emit_bitcode", @@ -812,6 +835,11 @@ pub(crate) unsafe fn codegen( } let result = llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback); + + if result == llvm::LLVMRustResult::Success { + record_artifact_size(&cgcx.prof, "llvm_ir", &out); + } + result.into_result().map_err(|()| { let msg = format!("failed to write LLVM IR to {}", out.display()); llvm_err(diag_handler, &msg) @@ -842,6 +870,7 @@ pub(crate) unsafe fn codegen( &path, None, llvm::FileType::AssemblyFile, + &cgcx.prof, ) })?; } @@ -875,6 +904,7 @@ pub(crate) unsafe fn codegen( &obj_out, dwo_out, llvm::FileType::ObjectFile, + &cgcx.prof, ) })?; } @@ -1131,3 +1161,19 @@ fn create_msvc_imps( symbol_name.starts_with(b"__llvm_profile_") } } + +fn record_artifact_size( + self_profiler_ref: &SelfProfilerRef, + artifact_kind: &'static str, + path: &Path, +) { + // Don't stat the file if we are not going to record its size. + if !self_profiler_ref.enabled() { + return; + } + + if let Some(artifact_name) = path.file_name() { + let file_size = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0); + self_profiler_ref.artifact_size(artifact_kind, artifact_name.to_string_lossy(), file_size); + } +} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 6c02543bd7cc4..1ba0c4fa05b5b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -121,6 +121,19 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( if sess.opts.json_artifact_notifications { sess.parse_sess.span_diagnostic.emit_artifact_notification(&out_filename, "link"); } + + if sess.prof.enabled() { + if let Some(artifact_name) = out_filename.file_name() { + // Record size for self-profiling + let file_size = std::fs::metadata(&out_filename).map(|m| m.len()).unwrap_or(0); + + sess.prof.artifact_size( + "linked_artifact", + artifact_name.to_string_lossy(), + file_size, + ); + } + } } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 0dbef66ac37d7..49c3472a20266 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2187,5 +2187,8 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { result[header + 2] = (pos >> 8) as u8; result[header + 3] = (pos >> 0) as u8; + // Record metadata size for self-profiling + tcx.prof.artifact_size("crate_metadata", "crate_metadata", result.len() as u64); + EncodedMetadata { raw_data: result } } diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index 7a7a56a034ed2..658c9028ca1a1 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -361,6 +361,17 @@ fn collect_and_partition_mono_items<'tcx>( ) }); + if tcx.prof.enabled() { + // Record CGU size estimates for self-profiling. + for cgu in codegen_units { + tcx.prof.artifact_size( + "codegen_unit_size_estimate", + &cgu.name().as_str()[..], + cgu.size_estimate() as u64, + ); + } + } + let mono_items: DefIdSet = items .iter() .filter_map(|mono_item| match *mono_item { From db4e60b29f863bbf78351b7f095b221ffe7b93a6 Mon Sep 17 00:00:00 2001 From: asquared31415 <34665709+asquared31415@users.noreply.github.com> Date: Tue, 9 Nov 2021 07:13:53 -0500 Subject: [PATCH 16/17] document Box and box_free connection --- library/alloc/src/boxed.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index bd4f52560421b..f6332b072cf30 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -169,6 +169,9 @@ use crate::vec::Vec; #[lang = "owned_box"] #[fundamental] #[stable(feature = "rust1", since = "1.0.0")] +// The declaration of the `Box` struct must be kept in sync with the +// `alloc::alloc::box_free` function or ICEs will happen. See the comment +// on `box_free` for more details. pub struct Box< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, From 10d65a9636907ff5555307e9a3ebe0123ca2c3e6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 5 Nov 2021 15:58:14 +0100 Subject: [PATCH 17/17] Allow to run a specific rustdoc-js* test --- src/bootstrap/test.rs | 53 +++++++++++++--------------------- src/bootstrap/util.rs | 32 ++++++++++++++++++++ src/tools/rustdoc-js/tester.js | 18 ++++++++---- 3 files changed, 64 insertions(+), 39 deletions(-) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 8594fa42266b4..22bf6b8a9d4dc 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -763,7 +763,7 @@ impl Step for RustdocJSStd { const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/test/rustdoc-js-std") + run.suite_path("src/test/rustdoc-js-std") } fn make_run(run: RunConfig<'_>) { @@ -783,6 +783,17 @@ impl Step for RustdocJSStd { .arg(builder.doc_out(self.target)) .arg("--test-folder") .arg(builder.src.join("src/test/rustdoc-js-std")); + for path in &builder.paths { + if let Some(p) = + util::is_valid_test_suite_arg(path, "src/test/rustdoc-js-std", builder) + { + if !p.ends_with(".js") { + eprintln!("A non-js file was given: `{}`", path.display()); + panic!("Cannot run rustdoc-js-std tests"); + } + command.arg("--test-file").arg(path); + } + } builder.ensure(crate::doc::Std { target: self.target, stage: builder.top_stage }); builder.run(&mut command); } else { @@ -803,7 +814,7 @@ impl Step for RustdocJSNotStd { const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/test/rustdoc-js") + run.suite_path("src/test/rustdoc-js") } fn make_run(run: RunConfig<'_>) { @@ -938,8 +949,12 @@ impl Step for RustdocGUI { .arg("--tests-folder") .arg(builder.build.src.join("src/test/rustdoc-gui")); for path in &builder.paths { - if let Some(name) = path.file_name().and_then(|f| f.to_str()) { - if name.ends_with(".goml") { + if let Some(p) = util::is_valid_test_suite_arg(path, "src/test/rustdoc-gui", builder) { + if !p.ends_with(".goml") { + eprintln!("A non-goml file was given: `{}`", path.display()); + panic!("Cannot run rustdoc-gui tests"); + } + if let Some(name) = path.file_name().and_then(|f| f.to_str()) { command.arg("--file").arg(name); } } @@ -1416,35 +1431,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the // Get test-args by striping suite path let mut test_args: Vec<&str> = paths .iter() - .map(|p| match p.strip_prefix(".") { - Ok(path) => path, - Err(_) => p, - }) - .filter(|p| p.starts_with(suite_path)) - .filter(|p| { - let exists = p.is_dir() || p.is_file(); - if !exists { - if let Some(p) = p.to_str() { - builder.info(&format!( - "Warning: Skipping \"{}\": not a regular file or directory", - p - )); - } - } - exists - }) - .filter_map(|p| { - // Since test suite paths are themselves directories, if we don't - // specify a directory or file, we'll get an empty string here - // (the result of the test suite directory without its suite prefix). - // Therefore, we need to filter these out, as only the first --test-args - // flag is respected, so providing an empty --test-args conflicts with - // any following it. - match p.strip_prefix(suite_path).ok().and_then(|p| p.to_str()) { - Some(s) if !s.is_empty() => Some(s), - _ => None, - } - }) + .filter_map(|p| util::is_valid_test_suite_arg(p, suite_path, builder)) .collect(); test_args.append(&mut builder.config.cmd.test_args()); diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index 112979b0bebc8..57178aa382ffd 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -310,3 +310,35 @@ pub fn use_host_linker(target: TargetSelection) -> bool { || target.contains("fuchsia") || target.contains("bpf")) } + +pub fn is_valid_test_suite_arg<'a, P: AsRef>( + path: &'a Path, + suite_path: P, + builder: &Builder<'_>, +) -> Option<&'a str> { + let suite_path = suite_path.as_ref(); + let path = match path.strip_prefix(".") { + Ok(p) => p, + Err(_) => path, + }; + if !path.starts_with(suite_path) { + return None; + } + let exists = path.is_dir() || path.is_file(); + if !exists { + if let Some(p) = path.to_str() { + builder.info(&format!("Warning: Skipping \"{}\": not a regular file or directory", p)); + } + return None; + } + // Since test suite paths are themselves directories, if we don't + // specify a directory or file, we'll get an empty string here + // (the result of the test suite directory without its suite prefix). + // Therefore, we need to filter these out, as only the first --test-args + // flag is respected, so providing an empty --test-args conflicts with + // any following it. + match path.strip_prefix(suite_path).ok().and_then(|p| p.to_str()) { + Some(s) if !s.is_empty() => Some(s), + _ => None, + } +} diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index a673e425dfff9..4f73a7f634098 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -401,7 +401,8 @@ function showHelp() { console.log(" --doc-folder [PATH] : location of the generated doc folder"); console.log(" --help : show this message then quit"); console.log(" --crate-name [STRING] : crate name to be used"); - console.log(" --test-file [PATH] : location of the JS test file"); + console.log(" --test-file [PATHs] : location of the JS test files (can be called " + + "multiple times)"); console.log(" --test-folder [PATH] : location of the JS tests folder"); console.log(" --resource-suffix [STRING] : suffix to refer to the correct files"); } @@ -412,7 +413,7 @@ function parseOptions(args) { "resource_suffix": "", "doc_folder": "", "test_folder": "", - "test_file": "", + "test_file": [], }; var correspondences = { "--resource-suffix": "resource_suffix", @@ -429,7 +430,11 @@ function parseOptions(args) { console.log("Missing argument after `" + args[i - 1] + "` option."); return null; } - opts[correspondences[args[i - 1]]] = args[i]; + if (args[i - 1] !== "--test-file") { + opts[correspondences[args[i - 1]]] = args[i]; + } else { + opts[correspondences[args[i - 1]]].push(args[i]); + } } else if (args[i] === "--help") { showHelp(); process.exit(0); @@ -471,9 +476,10 @@ function main(argv) { var errors = 0; if (opts["test_file"].length !== 0) { - errors += checkFile(opts["test_file"], opts, loaded, index); - } - if (opts["test_folder"].length !== 0) { + opts["test_file"].forEach(function(file) { + errors += checkFile(file, opts, loaded, index); + }); + } else if (opts["test_folder"].length !== 0) { fs.readdirSync(opts["test_folder"]).forEach(function(file) { if (!file.endsWith(".js")) { return;