Skip to content

Commit

Permalink
Rollup merge of rust-lang#75069 - lcnr:type-of-lazy-norm, r=varkor
Browse files Browse the repository at this point in the history
move const param structural match checks to wfcheck

fixes rust-lang#75047 fixes rust-lang#74950

We currently check for structural match violations inside of `type_of`.
As we need to check the array length when checking if `[NonEq; arr_len]` is structural match, we potentially require the variance of an expression. Computing the variance requires `type_of` for all types though, resulting in a cycle error.

r? @varkor @eddyb
  • Loading branch information
tmandry authored Aug 19, 2020
2 parents 443e177 + 7542615 commit f0b7901
Show file tree
Hide file tree
Showing 31 changed files with 292 additions and 469 deletions.
137 changes: 135 additions & 2 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit as hir_visit;
use rustc_hir::intravisit::Visitor;
use rustc_hir::itemlikevisit::ParItemLikeVisitor;
use rustc_hir::lang_items;
use rustc_hir::ItemKind;
use rustc_middle::hir::map as hir_map;
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{
Expand Down Expand Up @@ -275,6 +278,107 @@ pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
check_associated_item(tcx, impl_item.hir_id, impl_item.span, method_sig);
}

fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
match param.kind {
// We currently only check wf of const params here.
hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => (),

// Const parameters are well formed if their
// type is structural match.
hir::GenericParamKind::Const { ty: hir_ty } => {
let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id));

let err_ty_str;
let mut is_ptr = true;
let err = if tcx.features().min_const_generics {
match ty.kind {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => {
is_ptr = false;
err_ty_str = format!("`{}`", ty);
Some(err_ty_str.as_str())
}
}
} else {
match ty.peel_refs().kind {
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => None,
}
};
if let Some(unsupported_type) = err {
if is_ptr {
tcx.sess.span_err(
hir_ty.span,
&format!(
"using {} as const generic parameters is forbidden",
unsupported_type
),
)
} else {
tcx.sess
.struct_span_err(
hir_ty.span,
&format!(
"{} is forbidden as the type of a const generic parameter",
unsupported_type
),
)
.note("the only supported types are integers, `bool` and `char`")
.note("more complex types are supported with `#[feature(const_generics)]`")
.emit()
}
};

if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty)
.is_some()
{
// We use the same error code in both branches, because this is really the same
// issue: we just special-case the message for type parameters to make it
// clearer.
if let ty::Param(_) = ty.peel_refs().kind {
// Const parameters may not have type parameters as their types,
// because we cannot be sure that the type parameter derives `PartialEq`
// and `Eq` (just implementing them is not enough for `structural_match`).
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
used as the type of a const parameter",
ty,
)
.span_label(
hir_ty.span,
format!("`{}` may not derive both `PartialEq` and `Eq`", ty),
)
.note(
"it is not currently possible to use a type parameter as the type of a \
const parameter",
)
.emit();
} else {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
the type of a const parameter",
ty,
)
.span_label(
hir_ty.span,
format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty),
)
.emit();
}
}
}
}
}

fn check_associated_item(
tcx: TyCtxt<'_>,
item_id: hir::HirId,
Expand Down Expand Up @@ -1282,6 +1386,7 @@ fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, span: Span, id: hir::HirId) {
fcx.select_all_obligations_or_error();
}

#[derive(Clone, Copy)]
pub struct CheckTypeWellFormedVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
}
Expand All @@ -1294,21 +1399,49 @@ impl CheckTypeWellFormedVisitor<'tcx> {

impl ParItemLikeVisitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> {
fn visit_item(&self, i: &'tcx hir::Item<'tcx>) {
Visitor::visit_item(&mut self.clone(), i);
}

fn visit_trait_item(&self, trait_item: &'tcx hir::TraitItem<'tcx>) {
Visitor::visit_trait_item(&mut self.clone(), trait_item);
}

fn visit_impl_item(&self, impl_item: &'tcx hir::ImplItem<'tcx>) {
Visitor::visit_impl_item(&mut self.clone(), impl_item);
}
}

impl Visitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> {
type Map = hir_map::Map<'tcx>;

fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
hir_visit::NestedVisitorMap::OnlyBodies(self.tcx.hir())
}

fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
debug!("visit_item: {:?}", i);
let def_id = self.tcx.hir().local_def_id(i.hir_id);
self.tcx.ensure().check_item_well_formed(def_id);
hir_visit::walk_item(self, i);
}

fn visit_trait_item(&self, trait_item: &'tcx hir::TraitItem<'tcx>) {
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
debug!("visit_trait_item: {:?}", trait_item);
let def_id = self.tcx.hir().local_def_id(trait_item.hir_id);
self.tcx.ensure().check_trait_item_well_formed(def_id);
hir_visit::walk_trait_item(self, trait_item);
}

fn visit_impl_item(&self, impl_item: &'tcx hir::ImplItem<'tcx>) {
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
debug!("visit_impl_item: {:?}", impl_item);
let def_id = self.tcx.hir().local_def_id(impl_item.hir_id);
self.tcx.ensure().check_impl_item_well_formed(def_id);
hir_visit::walk_impl_item(self, impl_item);
}

fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
check_param_wf(self.tcx, p);
hir_visit::walk_generic_param(self, p);
}
}

Expand Down
85 changes: 2 additions & 83 deletions src/librustc_typeck/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable};
use rustc_span::symbol::Ident;
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::traits;

use super::ItemCtxt;
use super::{bad_placeholder_type, is_suggestable_infer_ty};
Expand Down Expand Up @@ -323,88 +322,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
}

Node::GenericParam(param) => match &param.kind {
GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty),
GenericParamKind::Const { ty: ref hir_ty, .. } => {
let ty = icx.to_ty(hir_ty);
let err_ty_str;
let err = if tcx.features().min_const_generics {
match ty.kind {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => {
err_ty_str = format!("`{}`", ty);
Some(err_ty_str.as_str())
}
}
} else {
match ty.peel_refs().kind {
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => None,
}
};
if let Some(unsupported_type) = err {
let mut err = tcx.sess.struct_span_err(
hir_ty.span,
&format!(
"using {} as const generic parameters is forbidden",
unsupported_type
),
);

if tcx.features().min_const_generics {
err.note("the only supported types are integers, `bool` and `char`")
.note("more complex types are supported with `#[feature(const_generics)]`").emit()
} else {
err.emit();
}
};
if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty)
.is_some()
{
// We use the same error code in both branches, because this is really the same
// issue: we just special-case the message for type parameters to make it
// clearer.
if let ty::Param(_) = ty.peel_refs().kind {
// Const parameters may not have type parameters as their types,
// because we cannot be sure that the type parameter derives `PartialEq`
// and `Eq` (just implementing them is not enough for `structural_match`).
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
used as the type of a const parameter",
ty,
)
.span_label(
hir_ty.span,
format!("`{}` may not derive both `PartialEq` and `Eq`", ty),
)
.note(
"it is not currently possible to use a type parameter as the type of a \
const parameter",
)
.emit();
} else {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
the type of a const parameter",
ty,
)
.span_label(
hir_ty.span,
format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty),
)
.emit();
}
}
ty
}
GenericParamKind::Type { default: Some(ty), .. }
| GenericParamKind::Const { ty, .. } => icx.to_ty(ty),
x => bug!("unexpected non-type Node::GenericParam: {:?}", x),
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ LL | arr: [u8; CFG.arr_size],
|
= help: it is currently only allowed to use either `CFG` or `{ CFG }` as generic constants

error: using `Config` as const generic parameters is forbidden
error: `Config` is forbidden as the type of a const generic parameter
--> $DIR/array-size-in-generic-struct-param.rs:18:21
|
LL | struct B<const CFG: Config> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct Config {
}

struct B<const CFG: Config> {
//[min]~^ ERROR using `Config` as const generic parameters is forbidden
//[min]~^ ERROR `Config` is forbidden
arr: [u8; CFG.arr_size],
//[full]~^ ERROR constant expression depends on a generic parameter
//[min]~^^ ERROR generic parameters must not be used inside of non trivial
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ error[E0637]: `&` without an explicit lifetime name cannot be used here
LL | fn bar<const N: &u8>() {}
| ^ explicit lifetime name needed here

error: using `&'static u8` as const generic parameters is forbidden
error: `&'static u8` is forbidden as the type of a const generic parameter
--> $DIR/const-param-elided-lifetime.rs:11:19
|
LL | struct A<const N: &u8>;
Expand All @@ -37,7 +37,7 @@ LL | struct A<const N: &u8>;
= note: the only supported types are integers, `bool` and `char`
= note: more complex types are supported with `#[feature(const_generics)]`

error: using `&'static u8` as const generic parameters is forbidden
error: `&'static u8` is forbidden as the type of a const generic parameter
--> $DIR/const-param-elided-lifetime.rs:16:15
|
LL | impl<const N: &u8> A<N> {
Expand All @@ -46,7 +46,7 @@ LL | impl<const N: &u8> A<N> {
= note: the only supported types are integers, `bool` and `char`
= note: more complex types are supported with `#[feature(const_generics)]`

error: using `&'static u8` as const generic parameters is forbidden
error: `&'static u8` is forbidden as the type of a const generic parameter
--> $DIR/const-param-elided-lifetime.rs:24:15
|
LL | impl<const N: &u8> B for A<N> {}
Expand All @@ -55,7 +55,7 @@ LL | impl<const N: &u8> B for A<N> {}
= note: the only supported types are integers, `bool` and `char`
= note: more complex types are supported with `#[feature(const_generics)]`

error: using `&'static u8` as const generic parameters is forbidden
error: `&'static u8` is forbidden as the type of a const generic parameter
--> $DIR/const-param-elided-lifetime.rs:28:17
|
LL | fn bar<const N: &u8>() {}
Expand All @@ -64,7 +64,7 @@ LL | fn bar<const N: &u8>() {}
= note: the only supported types are integers, `bool` and `char`
= note: more complex types are supported with `#[feature(const_generics)]`

error: using `&'static u8` as const generic parameters is forbidden
error: `&'static u8` is forbidden as the type of a const generic parameter
--> $DIR/const-param-elided-lifetime.rs:19:21
|
LL | fn foo<const M: &u8>(&self) {}
Expand Down
10 changes: 5 additions & 5 deletions src/test/ui/const-generics/const-param-elided-lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@

struct A<const N: &u8>;
//~^ ERROR `&` without an explicit lifetime name cannot be used here
//[min]~^^ ERROR using `&'static u8` as const generic parameters is forbidden
//[min]~^^ ERROR `&'static u8` is forbidden
trait B {}

impl<const N: &u8> A<N> {
//~^ ERROR `&` without an explicit lifetime name cannot be used here
//[min]~^^ ERROR using `&'static u8` as const generic parameters is forbidden
//[min]~^^ ERROR `&'static u8` is forbidden
fn foo<const M: &u8>(&self) {}
//~^ ERROR `&` without an explicit lifetime name cannot be used here
//[min]~^^ ERROR using `&'static u8` as const generic parameters is forbidden
//[min]~^^ ERROR `&'static u8` is forbidden
}

impl<const N: &u8> B for A<N> {}
//~^ ERROR `&` without an explicit lifetime name cannot be used here
//[min]~^^ ERROR using `&'static u8` as const generic parameters is forbidden
//[min]~^^ ERROR `&'static u8` is forbidden

fn bar<const N: &u8>() {}
//~^ ERROR `&` without an explicit lifetime name cannot be used here
//[min]~^^ ERROR using `&'static u8` as const generic parameters is forbidden
//[min]~^^ ERROR `&'static u8` is forbidden

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ error[E0770]: the type of const parameters must not depend on other generic para
LL | pub struct SelfDependent<const N: [u8; N]>;
| ^ the type must not depend on the parameter `N`

error: using `[u8; _]` as const generic parameters is forbidden
error: `[u8; _]` is forbidden as the type of a const generic parameter
--> $DIR/const-param-type-depends-on-const-param.rs:12:47
|
LL | pub struct Dependent<const N: usize, const X: [u8; N]>([(); N]);
Expand All @@ -19,7 +19,7 @@ LL | pub struct Dependent<const N: usize, const X: [u8; N]>([(); N]);
= note: the only supported types are integers, `bool` and `char`
= note: more complex types are supported with `#[feature(const_generics)]`

error: using `[u8; _]` as const generic parameters is forbidden
error: `[u8; _]` is forbidden as the type of a const generic parameter
--> $DIR/const-param-type-depends-on-const-param.rs:16:35
|
LL | pub struct SelfDependent<const N: [u8; N]>;
Expand Down
Loading

0 comments on commit f0b7901

Please sign in to comment.