Rollup merge of #79019 - lcnr:generic-arg-validation, r=petrochenkov

astconv: extract closures into a separate trait

Am currently looking into completely removing `check_generic_arg_count` and `create_substs_for_generic_args` was somewhat difficult to understand for me so I moved these closures into a trait.

This should not have changed the behavior of any of these methods
This commit is contained in:
Jonas Schievink 2020-11-15 13:39:50 +01:00 committed by GitHub
commit 00396cb23a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 298 additions and 184 deletions

View file

@ -1,12 +1,13 @@
use crate::astconv::{
AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition,
AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
GenericArgCountResult, GenericArgPosition,
};
use crate::errors::AssocTypeBindingNotAllowed;
use rustc_ast::ast::ParamKindOrd;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{GenericArg, GenericArgs};
use rustc_hir::GenericArg;
use rustc_middle::ty::{
self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt,
};
@ -90,20 +91,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
/// instantiate a `GenericArg`.
/// - `inferred_kind`: if no parameter was provided, and inference is enabled, then
/// creates a suitable inference variable.
pub fn create_substs_for_generic_args<'b>(
pub fn create_substs_for_generic_args<'a>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
parent_substs: &[subst::GenericArg<'tcx>],
has_self: bool,
self_ty: Option<Ty<'tcx>>,
arg_count: GenericArgCountResult,
args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool),
mut provided_kind: impl FnMut(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>,
mut inferred_kind: impl FnMut(
Option<&[subst::GenericArg<'tcx>]>,
&GenericParamDef,
bool,
) -> subst::GenericArg<'tcx>,
ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>,
) -> SubstsRef<'tcx> {
// Collect the segments of the path; we need to substitute arguments
// for parameters throughout the entire path (wherever there are
@ -142,7 +137,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
substs.push(
self_ty
.map(|ty| ty.into())
.unwrap_or_else(|| inferred_kind(None, param, true)),
.unwrap_or_else(|| ctx.inferred_kind(None, param, true)),
);
params.next();
}
@ -151,7 +146,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
// Check whether this segment takes generic arguments and the user has provided any.
let (generic_args, infer_args) = args_for_def_id(def_id);
let (generic_args, infer_args) = ctx.args_for_def_id(def_id);
let mut args =
generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable();
@ -173,7 +168,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
(GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _)
| (GenericArg::Type(_), GenericParamDefKind::Type { .. }, _)
| (GenericArg::Const(_), GenericParamDefKind::Const, _) => {
substs.push(provided_kind(param, arg));
substs.push(ctx.provided_kind(param, arg));
args.next();
params.next();
}
@ -184,7 +179,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
) => {
// We expected a lifetime argument, but got a type or const
// argument. That means we're inferring the lifetimes.
substs.push(inferred_kind(None, param, infer_args));
substs.push(ctx.inferred_kind(None, param, infer_args));
force_infer_lt = Some(arg);
params.next();
}
@ -302,7 +297,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
(None, Some(&param)) => {
// If there are fewer arguments than parameters, it means
// we're inferring the remaining arguments.
substs.push(inferred_kind(Some(&substs), param, infer_args));
substs.push(ctx.inferred_kind(Some(&substs), param, infer_args));
params.next();
}

View file

@ -165,6 +165,23 @@ pub struct GenericArgCountResult {
pub correct: Result<(), GenericArgCountMismatch>,
}
pub trait CreateSubstsForGenericArgsCtxt<'a, 'tcx> {
fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'a>>, bool);
fn provided_kind(
&mut self,
param: &ty::GenericParamDef,
arg: &GenericArg<'_>,
) -> subst::GenericArg<'tcx>;
fn inferred_kind(
&mut self,
substs: Option<&[subst::GenericArg<'tcx>]>,
param: &ty::GenericParamDef,
infer_args: bool,
) -> subst::GenericArg<'tcx>;
}
impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
pub fn ast_region_to_region(
&self,
@ -321,81 +338,102 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
);
let is_object = self_ty.map_or(false, |ty| ty == self.tcx().types.trait_object_dummy_self);
let default_needs_object_self = |param: &ty::GenericParamDef| {
if let GenericParamDefKind::Type { has_default, .. } = param.kind {
if is_object && has_default {
let default_ty = tcx.at(span).type_of(param.def_id);
let self_param = tcx.types.self_param;
if default_ty.walk().any(|arg| arg == self_param.into()) {
// There is no suitable inference default for a type parameter
// that references self, in an object type.
return true;
struct SubstsForAstPathCtxt<'a, 'tcx> {
astconv: &'a (dyn AstConv<'tcx> + 'a),
def_id: DefId,
generic_args: &'a GenericArgs<'a>,
span: Span,
missing_type_params: Vec<String>,
inferred_params: Vec<Span>,
infer_args: bool,
is_object: bool,
}
impl<'tcx, 'a> SubstsForAstPathCtxt<'tcx, 'a> {
fn default_needs_object_self(&mut self, param: &ty::GenericParamDef) -> bool {
let tcx = self.astconv.tcx();
if let GenericParamDefKind::Type { has_default, .. } = param.kind {
if self.is_object && has_default {
let default_ty = tcx.at(self.span).type_of(param.def_id);
let self_param = tcx.types.self_param;
if default_ty.walk().any(|arg| arg == self_param.into()) {
// There is no suitable inference default for a type parameter
// that references self, in an object type.
return true;
}
}
}
false
}
}
false
};
let mut missing_type_params = vec![];
let mut inferred_params = vec![];
let substs = Self::create_substs_for_generic_args(
tcx,
def_id,
parent_substs,
self_ty.is_some(),
self_ty,
arg_count.clone(),
// Provide the generic args, and whether types should be inferred.
|did| {
if did == def_id {
(Some(generic_args), infer_args)
impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for SubstsForAstPathCtxt<'a, 'tcx> {
fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'a>>, bool) {
if did == self.def_id {
(Some(self.generic_args), self.infer_args)
} else {
// The last component of this tuple is unimportant.
(None, false)
}
},
// Provide substitutions for parameters for which (valid) arguments have been provided.
|param, arg| match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
self.ast_region_to_region(&lt, Some(param)).into()
}
(GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => {
if *has_default {
tcx.check_optional_stability(
param.def_id,
Some(arg.id()),
arg.span(),
|_, _| {
// Default generic parameters may not be marked
// with stability attributes, i.e. when the
// default parameter was defined at the same time
// as the rest of the type. As such, we ignore missing
// stability attributes.
}
fn provided_kind(
&mut self,
param: &ty::GenericParamDef,
arg: &GenericArg<'_>,
) -> subst::GenericArg<'tcx> {
let tcx = self.astconv.tcx();
match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
self.astconv.ast_region_to_region(&lt, Some(param)).into()
}
(&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => {
if has_default {
tcx.check_optional_stability(
param.def_id,
Some(arg.id()),
arg.span(),
|_, _| {
// Default generic parameters may not be marked
// with stability attributes, i.e. when the
// default parameter was defined at the same time
// as the rest of the type. As such, we ignore missing
// stability attributes.
},
)
}
if let (hir::TyKind::Infer, false) =
(&ty.kind, self.astconv.allow_ty_infer())
{
self.inferred_params.push(ty.span);
tcx.ty_error().into()
} else {
self.astconv.ast_ty_to_ty(&ty).into()
}
}
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
ty::Const::from_opt_const_arg_anon_const(
tcx,
ty::WithOptConstParam {
did: tcx.hir().local_def_id(ct.value.hir_id),
const_param_did: Some(param.def_id),
},
)
.into()
}
if let (hir::TyKind::Infer, false) = (&ty.kind, self.allow_ty_infer()) {
inferred_params.push(ty.span);
tcx.ty_error().into()
} else {
self.ast_ty_to_ty(&ty).into()
}
_ => unreachable!(),
}
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
ty::Const::from_opt_const_arg_anon_const(
tcx,
ty::WithOptConstParam {
did: tcx.hir().local_def_id(ct.value.hir_id),
const_param_did: Some(param.def_id),
},
)
.into()
}
_ => unreachable!(),
},
// Provide substitutions for parameters for which arguments are inferred.
|substs, param, infer_args| {
}
fn inferred_kind(
&mut self,
substs: Option<&[subst::GenericArg<'tcx>]>,
param: &ty::GenericParamDef,
infer_args: bool,
) -> subst::GenericArg<'tcx> {
let tcx = self.astconv.tcx();
match param.kind {
GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(),
GenericParamDefKind::Type { has_default, .. } => {
@ -407,48 +445,72 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// other type parameters may reference `Self` in their
// defaults. This will lead to an ICE if we are not
// careful!
if default_needs_object_self(param) {
missing_type_params.push(param.name.to_string());
if self.default_needs_object_self(param) {
self.missing_type_params.push(param.name.to_string());
tcx.ty_error().into()
} else {
// This is a default type parameter.
self.normalize_ty(
span,
tcx.at(span).type_of(param.def_id).subst_spanned(
tcx,
substs.unwrap(),
Some(span),
),
)
.into()
self.astconv
.normalize_ty(
self.span,
tcx.at(self.span).type_of(param.def_id).subst_spanned(
tcx,
substs.unwrap(),
Some(self.span),
),
)
.into()
}
} else if infer_args {
// No type parameters were provided, we can infer all.
let param =
if !default_needs_object_self(param) { Some(param) } else { None };
self.ty_infer(param, span).into()
let param = if !self.default_needs_object_self(param) {
Some(param)
} else {
None
};
self.astconv.ty_infer(param, self.span).into()
} else {
// We've already errored above about the mismatch.
tcx.ty_error().into()
}
}
GenericParamDefKind::Const => {
let ty = tcx.at(span).type_of(param.def_id);
let ty = tcx.at(self.span).type_of(param.def_id);
// FIXME(const_generics:defaults)
if infer_args {
// No const parameters were provided, we can infer all.
self.ct_infer(ty, Some(param), span).into()
self.astconv.ct_infer(ty, Some(param), self.span).into()
} else {
// We've already errored above about the mismatch.
tcx.const_error(ty).into()
}
}
}
},
}
}
let mut substs_ctx = SubstsForAstPathCtxt {
astconv: self,
def_id,
span,
generic_args,
missing_type_params: vec![],
inferred_params: vec![],
infer_args,
is_object,
};
let substs = Self::create_substs_for_generic_args(
tcx,
def_id,
parent_substs,
self_ty.is_some(),
self_ty,
arg_count.clone(),
&mut substs_ctx,
);
self.complain_about_missing_type_params(
missing_type_params,
substs_ctx.missing_type_params,
def_id,
span,
generic_args.args.is_empty(),

View file

@ -1,5 +1,6 @@
use crate::astconv::{
AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg,
AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
GenericArgCountResult, PathSeg,
};
use crate::check::callee::{self, DeferredCallResolution};
use crate::check::method::{self, MethodCallee, SelfSource};
@ -1298,6 +1299,94 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
},
};
struct CreateCtorSubstsContext<'a, 'tcx> {
fcx: &'a FnCtxt<'a, 'tcx>,
span: Span,
path_segs: &'a [PathSeg],
infer_args_for_err: &'a FxHashSet<usize>,
segments: &'a [hir::PathSegment<'a>],
}
impl<'tcx, 'a> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for CreateCtorSubstsContext<'a, 'tcx> {
fn args_for_def_id(
&mut self,
def_id: DefId,
) -> (Option<&'a hir::GenericArgs<'a>>, bool) {
if let Some(&PathSeg(_, index)) =
self.path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
{
// If we've encountered an `impl Trait`-related error, we're just
// going to infer the arguments for better error messages.
if !self.infer_args_for_err.contains(&index) {
// Check whether the user has provided generic arguments.
if let Some(ref data) = self.segments[index].args {
return (Some(data), self.segments[index].infer_args);
}
}
return (None, self.segments[index].infer_args);
}
(None, true)
}
fn provided_kind(
&mut self,
param: &ty::GenericParamDef,
arg: &GenericArg<'_>,
) -> subst::GenericArg<'tcx> {
match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
AstConv::ast_region_to_region(self.fcx, lt, Some(param)).into()
}
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
self.fcx.to_ty(ty).into()
}
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
self.fcx.const_arg_to_const(&ct.value, param.def_id).into()
}
_ => unreachable!(),
}
}
fn inferred_kind(
&mut self,
substs: Option<&[subst::GenericArg<'tcx>]>,
param: &ty::GenericParamDef,
infer_args: bool,
) -> subst::GenericArg<'tcx> {
let tcx = self.fcx.tcx();
match param.kind {
GenericParamDefKind::Lifetime => {
self.fcx.re_infer(Some(param), self.span).unwrap().into()
}
GenericParamDefKind::Type { has_default, .. } => {
if !infer_args && has_default {
// If we have a default, then we it doesn't matter that we're not
// inferring the type arguments: we provide the default where any
// is missing.
let default = tcx.type_of(param.def_id);
self.fcx
.normalize_ty(
self.span,
default.subst_spanned(tcx, substs.unwrap(), Some(self.span)),
)
.into()
} else {
// If no type arguments were provided, we have to infer them.
// This case also occurs as a result of some malformed input, e.g.
// a lifetime argument being given instead of a type parameter.
// Using inference instead of `Error` gives better error messages.
self.fcx.var_for_def(self.span, param)
}
}
GenericParamDefKind::Const => {
// FIXME(const_generics:defaults)
// No const parameters were provided, we have to infer them.
self.fcx.var_for_def(self.span, param)
}
}
}
}
let substs = self_ctor_substs.unwrap_or_else(|| {
AstConv::create_substs_for_generic_args(
tcx,
@ -1306,68 +1395,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
has_self,
self_ty,
arg_count,
// Provide the generic args, and whether types should be inferred.
|def_id| {
if let Some(&PathSeg(_, index)) =
path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
{
// If we've encountered an `impl Trait`-related error, we're just
// going to infer the arguments for better error messages.
if !infer_args_for_err.contains(&index) {
// Check whether the user has provided generic arguments.
if let Some(ref data) = segments[index].args {
return (Some(data), segments[index].infer_args);
}
}
return (None, segments[index].infer_args);
}
(None, true)
},
// Provide substitutions for parameters for which (valid) arguments have been provided.
|param, arg| match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
AstConv::ast_region_to_region(self, lt, Some(param)).into()
}
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
self.to_ty(ty).into()
}
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
self.const_arg_to_const(&ct.value, param.def_id).into()
}
_ => unreachable!(),
},
// Provide substitutions for parameters for which arguments are inferred.
|substs, param, infer_args| {
match param.kind {
GenericParamDefKind::Lifetime => {
self.re_infer(Some(param), span).unwrap().into()
}
GenericParamDefKind::Type { has_default, .. } => {
if !infer_args && has_default {
// If we have a default, then we it doesn't matter that we're not
// inferring the type arguments: we provide the default where any
// is missing.
let default = tcx.type_of(param.def_id);
self.normalize_ty(
span,
default.subst_spanned(tcx, substs.unwrap(), Some(span)),
)
.into()
} else {
// If no type arguments were provided, we have to infer them.
// This case also occurs as a result of some malformed input, e.g.
// a lifetime argument being given instead of a type parameter.
// Using inference instead of `Error` gives better error messages.
self.var_for_def(span, param)
}
}
GenericParamDefKind::Const => {
// FIXME(const_generics:defaults)
// No const parameters were provided, we have to infer them.
self.var_for_def(span, param)
}
}
&mut CreateCtorSubstsContext {
fcx: self,
span,
path_segs: &path_segs,
infer_args_for_err: &infer_args_for_err,
segments,
},
)
});

View file

@ -1,6 +1,6 @@
use super::{probe, MethodCallee};
use crate::astconv::AstConv;
use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt};
use crate::check::{callee, FnCtxt};
use crate::hir::def_id::DefId;
use crate::hir::GenericArg;
@ -10,7 +10,7 @@ use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::subst::{self, Subst, SubstsRef};
use rustc_middle::ty::{self, GenericParamDefKind, Ty};
use rustc_span::Span;
use rustc_trait_selection::traits;
@ -307,6 +307,52 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// parameters from the type and those from the method.
assert_eq!(generics.parent_count, parent_substs.len());
struct MethodSubstsCtxt<'a, 'tcx> {
cfcx: &'a ConfirmContext<'a, 'tcx>,
pick: &'a probe::Pick<'tcx>,
seg: &'a hir::PathSegment<'a>,
}
impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for MethodSubstsCtxt<'a, 'tcx> {
fn args_for_def_id(
&mut self,
def_id: DefId,
) -> (Option<&'a hir::GenericArgs<'a>>, bool) {
if def_id == self.pick.item.def_id {
if let Some(ref data) = self.seg.args {
return (Some(data), false);
}
}
(None, false)
}
fn provided_kind(
&mut self,
param: &ty::GenericParamDef,
arg: &GenericArg<'_>,
) -> subst::GenericArg<'tcx> {
match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
AstConv::ast_region_to_region(self.cfcx.fcx, lt, Some(param)).into()
}
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
self.cfcx.to_ty(ty).into()
}
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
self.cfcx.const_arg_to_const(&ct.value, param.def_id).into()
}
_ => unreachable!(),
}
}
fn inferred_kind(
&mut self,
_substs: Option<&[subst::GenericArg<'tcx>]>,
param: &ty::GenericParamDef,
_infer_args: bool,
) -> subst::GenericArg<'tcx> {
self.cfcx.var_for_def(self.cfcx.span, param)
}
}
AstConv::create_substs_for_generic_args(
self.tcx,
pick.item.def_id,
@ -314,29 +360,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
false,
None,
arg_count_correct,
// Provide the generic args, and whether types should be inferred.
|def_id| {
// The last component of the returned tuple here is unimportant.
if def_id == pick.item.def_id {
if let Some(ref data) = seg.args {
return (Some(data), false);
}
}
(None, false)
},
// Provide substitutions for parameters for which (valid) arguments have been provided.
|param, arg| match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
AstConv::ast_region_to_region(self.fcx, lt, Some(param)).into()
}
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => self.to_ty(ty).into(),
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
self.const_arg_to_const(&ct.value, param.def_id).into()
}
_ => unreachable!(),
},
// Provide substitutions for parameters for which arguments are inferred.
|_, param, _| self.var_for_def(self.span, param),
&mut MethodSubstsCtxt { cfcx: self, pick, seg },
)
}