mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Auto merge of #84299 - lcnr:const-generics-defaults-name-res, r=varkor
various const parameter defaults improvements Actually resolve names in const parameter defaults, fixing `struct Foo<const N: usize = { usize::MAX }>`. --- Split generic parameter ban rib for types and consts, allowing ```rust #![feature(const_generics_defaults)] struct Q; struct Foo<T = Q, const Q: usize = 3>(T); ``` --- Remove the type/const ordering restriction if `const_generics_defaults` is active, even if `const_generics` is not. allowing us to stabilize and test const param defaults separately. --- Check well formedness of const parameter defaults, eagerly emitting an error for `struct Foo<const N: usize = { 0 - 1 }>` --- Do not forbid const parameters in param defaults, allowing `struct Foo<const N: usize, T = [u8; N]>(T)` and `struct Foo<const N: usize, const M: usize = N>`. Note that this should not change anything which is stabilized, as on stable, type parameters must be in front of const parameters, which means that type parameter defaults are only allowed if no const parameters exist. We still forbid generic parameters inside of const param types. r? `@varkor` `@petrochenkov`
This commit is contained in:
commit
58bdb08947
@ -754,7 +754,7 @@ fn validate_generic_param_order(
|
||||
GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident),
|
||||
GenericParamKind::Const { ref ty, kw_span: _, default: _ } => {
|
||||
let ty = pprust::ty_to_string(ty);
|
||||
let unordered = sess.features_untracked().const_generics;
|
||||
let unordered = sess.features_untracked().unordered_const_ty_params();
|
||||
(ParamKindOrd::Const { unordered }, Some(format!("const {}: {}", param.ident, ty)))
|
||||
}
|
||||
};
|
||||
|
@ -63,6 +63,10 @@ macro_rules! declare_features {
|
||||
_ => panic!("`{}` was not listed in `declare_features`", feature),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unordered_const_ty_params(&self) -> bool {
|
||||
self.const_generics || self.const_generics_defaults
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -296,7 +296,9 @@ impl GenericArg<'_> {
|
||||
match self {
|
||||
GenericArg::Lifetime(_) => ast::ParamKindOrd::Lifetime,
|
||||
GenericArg::Type(_) => ast::ParamKindOrd::Type,
|
||||
GenericArg::Const(_) => ast::ParamKindOrd::Const { unordered: feats.const_generics },
|
||||
GenericArg::Const(_) => {
|
||||
ast::ParamKindOrd::Const { unordered: feats.unordered_const_ty_params() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ impl GenericParamDefKind {
|
||||
GenericParamDefKind::Lifetime => ast::ParamKindOrd::Lifetime,
|
||||
GenericParamDefKind::Type { .. } => ast::ParamKindOrd::Type,
|
||||
GenericParamDefKind::Const { .. } => {
|
||||
ast::ParamKindOrd::Const { unordered: tcx.features().const_generics }
|
||||
ast::ParamKindOrd::Const { unordered: tcx.features().unordered_const_ty_params() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -472,17 +472,6 @@ impl<'a> Resolver<'a> {
|
||||
);
|
||||
err
|
||||
}
|
||||
ResolutionError::ParamInAnonConstInTyDefault(name) => {
|
||||
let mut err = self.session.struct_span_err(
|
||||
span,
|
||||
"constant values inside of type parameter defaults must not depend on generic parameters",
|
||||
);
|
||||
err.span_label(
|
||||
span,
|
||||
format!("the anonymous constant must not depend on the parameter `{}`", name),
|
||||
);
|
||||
err
|
||||
}
|
||||
ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => {
|
||||
let mut err = self.session.struct_span_err(
|
||||
span,
|
||||
|
@ -555,18 +555,23 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
// provide previous type parameters as they're built. We
|
||||
// put all the parameters on the ban list and then remove
|
||||
// them one by one as they are processed and become available.
|
||||
let mut default_ban_rib = Rib::new(ForwardGenericParamBanRibKind);
|
||||
let mut found_default = false;
|
||||
default_ban_rib.bindings.extend(generics.params.iter().filter_map(
|
||||
|param| match param.kind {
|
||||
GenericParamKind::Type { default: Some(_), .. }
|
||||
| GenericParamKind::Const { default: Some(_), .. } => {
|
||||
found_default = true;
|
||||
Some((Ident::with_dummy_span(param.ident.name), Res::Err))
|
||||
let mut forward_ty_ban_rib = Rib::new(ForwardGenericParamBanRibKind);
|
||||
let mut forward_const_ban_rib = Rib::new(ForwardGenericParamBanRibKind);
|
||||
for param in generics.params.iter() {
|
||||
match param.kind {
|
||||
GenericParamKind::Type { .. } => {
|
||||
forward_ty_ban_rib
|
||||
.bindings
|
||||
.insert(Ident::with_dummy_span(param.ident.name), Res::Err);
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
));
|
||||
GenericParamKind::Const { .. } => {
|
||||
forward_const_ban_rib
|
||||
.bindings
|
||||
.insert(Ident::with_dummy_span(param.ident.name), Res::Err);
|
||||
}
|
||||
GenericParamKind::Lifetime => {}
|
||||
}
|
||||
}
|
||||
|
||||
// rust-lang/rust#61631: The type `Self` is essentially
|
||||
// another type parameter. For ADTs, we consider it
|
||||
@ -579,7 +584,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
// such as in the case of `trait Add<Rhs = Self>`.)
|
||||
if self.diagnostic_metadata.current_self_item.is_some() {
|
||||
// (`Some` if + only if we are in ADT's generics.)
|
||||
default_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
|
||||
forward_ty_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
|
||||
}
|
||||
|
||||
for param in &generics.params {
|
||||
@ -591,32 +596,38 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
}
|
||||
|
||||
if let Some(ref ty) = default {
|
||||
self.ribs[TypeNS].push(default_ban_rib);
|
||||
self.with_rib(ValueNS, ForwardGenericParamBanRibKind, |this| {
|
||||
// HACK: We use an empty `ForwardGenericParamBanRibKind` here which
|
||||
// is only used to forbid the use of const parameters inside of
|
||||
// type defaults.
|
||||
//
|
||||
// While the rib name doesn't really fit here, it does allow us to use the same
|
||||
// code for both const and type parameters.
|
||||
this.visit_ty(ty);
|
||||
});
|
||||
default_ban_rib = self.ribs[TypeNS].pop().unwrap();
|
||||
self.ribs[TypeNS].push(forward_ty_ban_rib);
|
||||
self.ribs[ValueNS].push(forward_const_ban_rib);
|
||||
self.visit_ty(ty);
|
||||
forward_const_ban_rib = self.ribs[ValueNS].pop().unwrap();
|
||||
forward_ty_ban_rib = self.ribs[TypeNS].pop().unwrap();
|
||||
}
|
||||
|
||||
// Allow all following defaults to refer to this type parameter.
|
||||
default_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name));
|
||||
forward_ty_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name));
|
||||
}
|
||||
GenericParamKind::Const { ref ty, kw_span: _, default: _ } => {
|
||||
// FIXME(const_generics_defaults): handle `default` value here
|
||||
for bound in ¶m.bounds {
|
||||
self.visit_param_bound(bound);
|
||||
}
|
||||
GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
|
||||
// Const parameters can't have param bounds.
|
||||
assert!(param.bounds.is_empty());
|
||||
|
||||
self.ribs[TypeNS].push(Rib::new(ConstParamTyRibKind));
|
||||
self.ribs[ValueNS].push(Rib::new(ConstParamTyRibKind));
|
||||
self.visit_ty(ty);
|
||||
self.ribs[TypeNS].pop().unwrap();
|
||||
self.ribs[ValueNS].pop().unwrap();
|
||||
|
||||
if let Some(ref expr) = default {
|
||||
self.ribs[TypeNS].push(forward_ty_ban_rib);
|
||||
self.ribs[ValueNS].push(forward_const_ban_rib);
|
||||
self.visit_anon_const(expr);
|
||||
forward_const_ban_rib = self.ribs[ValueNS].pop().unwrap();
|
||||
forward_ty_ban_rib = self.ribs[TypeNS].pop().unwrap();
|
||||
}
|
||||
|
||||
// Allow all following defaults to refer to this const parameter.
|
||||
forward_const_ban_rib
|
||||
.bindings
|
||||
.remove(&Ident::with_dummy_span(param.ident.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,8 +239,6 @@ enum ResolutionError<'a> {
|
||||
ForwardDeclaredTyParam, // FIXME(const_generics_defaults)
|
||||
/// ERROR E0770: the type of const parameters must not depend on other generic parameters.
|
||||
ParamInTyOfConstParam(Symbol),
|
||||
/// constant values inside of type parameter defaults must not depend on generic parameters.
|
||||
ParamInAnonConstInTyDefault(Symbol),
|
||||
/// generic parameters must not be used inside const evaluations.
|
||||
///
|
||||
/// This error is only emitted when using `min_const_generics`.
|
||||
@ -2672,26 +2670,18 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
}
|
||||
Res::Def(DefKind::TyParam, _) | Res::SelfTy(..) => {
|
||||
let mut in_ty_param_default = false;
|
||||
for rib in ribs {
|
||||
let has_generic_params = match rib.kind {
|
||||
let has_generic_params: HasGenericParams = match rib.kind {
|
||||
NormalRibKind
|
||||
| ClosureOrAsyncRibKind
|
||||
| AssocItemRibKind
|
||||
| ModuleRibKind(..)
|
||||
| MacroDefinition(..) => {
|
||||
| MacroDefinition(..)
|
||||
| ForwardGenericParamBanRibKind => {
|
||||
// Nothing to do. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
// We only forbid constant items if we are inside of type defaults,
|
||||
// for example `struct Foo<T, U = [u8; std::mem::size_of::<T>()]>`
|
||||
ForwardGenericParamBanRibKind => {
|
||||
// FIXME(const_generic_defaults): we may need to distinguish between
|
||||
// being in type parameter defaults and const parameter defaults
|
||||
in_ty_param_default = true;
|
||||
continue;
|
||||
}
|
||||
ConstantItemRibKind(trivial, _) => {
|
||||
let features = self.session.features_untracked();
|
||||
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
|
||||
@ -2720,19 +2710,7 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if in_ty_param_default {
|
||||
if record_used {
|
||||
self.report_error(
|
||||
span,
|
||||
ResolutionError::ParamInAnonConstInTyDefault(
|
||||
rib_ident.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
return Res::Err;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// This was an attempt to use a type parameter outside its scope.
|
||||
@ -2770,23 +2748,15 @@ impl<'a> Resolver<'a> {
|
||||
ribs.next();
|
||||
}
|
||||
|
||||
let mut in_ty_param_default = false;
|
||||
for rib in ribs {
|
||||
let has_generic_params = match rib.kind {
|
||||
NormalRibKind
|
||||
| ClosureOrAsyncRibKind
|
||||
| AssocItemRibKind
|
||||
| ModuleRibKind(..)
|
||||
| MacroDefinition(..) => continue,
|
||||
| MacroDefinition(..)
|
||||
| ForwardGenericParamBanRibKind => continue,
|
||||
|
||||
// We only forbid constant items if we are inside of type defaults,
|
||||
// for example `struct Foo<T, U = [u8; std::mem::size_of::<T>()]>`
|
||||
ForwardGenericParamBanRibKind => {
|
||||
// FIXME(const_generic_defaults): we may need to distinguish between
|
||||
// being in type parameter defaults and const parameter defaults
|
||||
in_ty_param_default = true;
|
||||
continue;
|
||||
}
|
||||
ConstantItemRibKind(trivial, _) => {
|
||||
let features = self.session.features_untracked();
|
||||
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
|
||||
@ -2808,19 +2778,7 @@ impl<'a> Resolver<'a> {
|
||||
return Res::Err;
|
||||
}
|
||||
|
||||
if in_ty_param_default {
|
||||
if record_used {
|
||||
self.report_error(
|
||||
span,
|
||||
ResolutionError::ParamInAnonConstInTyDefault(
|
||||
rib_ident.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
return Res::Err;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemRibKind(has_generic_params) => has_generic_params,
|
||||
|
@ -286,7 +286,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
ParamKindOrd::Const {
|
||||
unordered: tcx
|
||||
.features()
|
||||
.const_generics,
|
||||
.unordered_const_ty_params(),
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -309,7 +309,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
GenericArg::Lifetime(_) => ParamKindOrd::Lifetime,
|
||||
GenericArg::Type(_) => ParamKindOrd::Type,
|
||||
GenericArg::Const(_) => ParamKindOrd::Const {
|
||||
unordered: tcx.features().const_generics,
|
||||
unordered: tcx
|
||||
.features()
|
||||
.unordered_const_ty_params(),
|
||||
},
|
||||
}),
|
||||
Some(&format!(
|
||||
|
@ -513,7 +513,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
GenericParamDefKind::Const { has_default } => {
|
||||
let ty = tcx.at(self.span).type_of(param.def_id);
|
||||
if !infer_args && has_default {
|
||||
tcx.const_param_default(param.def_id).into()
|
||||
tcx.const_param_default(param.def_id)
|
||||
.subst_spanned(tcx, substs.unwrap(), Some(self.span))
|
||||
.into()
|
||||
} else {
|
||||
if infer_args {
|
||||
self.astconv.ct_infer(ty, Some(param), self.span).into()
|
||||
|
@ -1446,7 +1446,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
GenericParamDefKind::Const { has_default, .. } => {
|
||||
if !infer_args && has_default {
|
||||
tcx.const_param_default(param.def_id).into()
|
||||
tcx.const_param_default(param.def_id)
|
||||
.subst_spanned(tcx, substs.unwrap(), Some(self.span))
|
||||
.into()
|
||||
} else {
|
||||
self.fcx.var_for_def(self.span, param)
|
||||
}
|
||||
|
@ -728,20 +728,36 @@ fn check_where_clauses<'tcx, 'fcx>(
|
||||
//
|
||||
// Here, the default `Vec<[u32]>` is not WF because `[u32]: Sized` does not hold.
|
||||
for param in &generics.params {
|
||||
if let GenericParamDefKind::Type { .. } = param.kind {
|
||||
if is_our_default(¶m) {
|
||||
let ty = fcx.tcx.type_of(param.def_id);
|
||||
// Ignore dependent defaults -- that is, where the default of one type
|
||||
// parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
|
||||
// be sure if it will error or not as user might always specify the other.
|
||||
if !ty.needs_subst() {
|
||||
match param.kind {
|
||||
GenericParamDefKind::Type { .. } => {
|
||||
if is_our_default(¶m) {
|
||||
let ty = fcx.tcx.type_of(param.def_id);
|
||||
// Ignore dependent defaults -- that is, where the default of one type
|
||||
// parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
|
||||
// be sure if it will error or not as user might always specify the other.
|
||||
if !ty.needs_subst() {
|
||||
fcx.register_wf_obligation(
|
||||
ty.into(),
|
||||
fcx.tcx.def_span(param.def_id),
|
||||
ObligationCauseCode::MiscObligation,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
GenericParamDefKind::Const { .. } => {
|
||||
// FIXME(const_generics_defaults): Figure out if this
|
||||
// is the behavior we want, see the comment further below.
|
||||
if is_our_default(¶m) {
|
||||
let default_ct = tcx.const_param_default(param.def_id);
|
||||
fcx.register_wf_obligation(
|
||||
ty.into(),
|
||||
default_ct.into(),
|
||||
fcx.tcx.def_span(param.def_id),
|
||||
ObligationCauseCode::MiscObligation,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Doesn't have defaults.
|
||||
GenericParamDefKind::Lifetime => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -774,14 +790,25 @@ fn check_where_clauses<'tcx, 'fcx>(
|
||||
fcx.tcx.mk_param_from_def(param)
|
||||
}
|
||||
GenericParamDefKind::Const { .. } => {
|
||||
// FIXME(const_generics_defaults): I(@lcnr) feel like always
|
||||
// using the const parameter is the right choice here, even
|
||||
// if it needs substs.
|
||||
//
|
||||
// Before stabilizing this we probably want to get some tests
|
||||
// where this makes a difference and figure out what's the exact
|
||||
// behavior we want here.
|
||||
|
||||
// If the param has a default, ...
|
||||
if is_our_default(param) {
|
||||
let default_ct = tcx.const_param_default(param.def_id);
|
||||
// Const params currently have to be concrete.
|
||||
assert!(!default_ct.needs_subst());
|
||||
default_ct.into()
|
||||
} else {
|
||||
fcx.tcx.mk_param_from_def(param)
|
||||
// ... and it's not a dependent default, ...
|
||||
if !default_ct.needs_subst() {
|
||||
// ... then substitute it with the default.
|
||||
return default_ct.into();
|
||||
}
|
||||
}
|
||||
|
||||
fcx.tcx.mk_param_from_def(param)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1327,13 +1327,13 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
|
||||
}
|
||||
}
|
||||
|
||||
struct AnonConstInParamListDetector {
|
||||
in_param_list: bool,
|
||||
found_anon_const_in_list: bool,
|
||||
struct AnonConstInParamTyDetector {
|
||||
in_param_ty: bool,
|
||||
found_anon_const_in_param_ty: bool,
|
||||
ct: HirId,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for AnonConstInParamListDetector {
|
||||
impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
|
||||
type Map = intravisit::ErasedMap<'v>;
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
@ -1341,15 +1341,17 @@ impl<'v> Visitor<'v> for AnonConstInParamListDetector {
|
||||
}
|
||||
|
||||
fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
|
||||
let prev = self.in_param_list;
|
||||
self.in_param_list = true;
|
||||
intravisit::walk_generic_param(self, p);
|
||||
self.in_param_list = prev;
|
||||
if let GenericParamKind::Const { ref ty, default: _ } = p.kind {
|
||||
let prev = self.in_param_ty;
|
||||
self.in_param_ty = true;
|
||||
self.visit_ty(ty);
|
||||
self.in_param_ty = prev;
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_anon_const(&mut self, c: &'v hir::AnonConst) {
|
||||
if self.in_param_list && self.ct == c.hir_id {
|
||||
self.found_anon_const_in_list = true;
|
||||
if self.in_param_ty && self.ct == c.hir_id {
|
||||
self.found_anon_const_in_param_ty = true;
|
||||
} else {
|
||||
intravisit::walk_anon_const(self, c)
|
||||
}
|
||||
@ -1377,27 +1379,24 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
||||
let parent_id = tcx.hir().get_parent_item(hir_id);
|
||||
let parent_def_id = tcx.hir().local_def_id(parent_id);
|
||||
|
||||
let mut in_param_list = false;
|
||||
let mut in_param_ty = false;
|
||||
for (_parent, node) in tcx.hir().parent_iter(hir_id) {
|
||||
if let Some(generics) = node.generics() {
|
||||
let mut visitor = AnonConstInParamListDetector {
|
||||
in_param_list: false,
|
||||
found_anon_const_in_list: false,
|
||||
let mut visitor = AnonConstInParamTyDetector {
|
||||
in_param_ty: false,
|
||||
found_anon_const_in_param_ty: false,
|
||||
ct: hir_id,
|
||||
};
|
||||
|
||||
visitor.visit_generics(generics);
|
||||
in_param_list = visitor.found_anon_const_in_list;
|
||||
in_param_ty = visitor.found_anon_const_in_param_ty;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if in_param_list {
|
||||
if in_param_ty {
|
||||
// We do not allow generic parameters in anon consts if we are inside
|
||||
// of a param list.
|
||||
//
|
||||
// This affects both default type bindings, e.g. `struct<T, U = [u8; std::mem::size_of::<T>()]>(T, U)`,
|
||||
// and the types of const parameters, e.g. `struct V<const N: usize, const M: [u8; N]>();`.
|
||||
// of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
|
||||
None
|
||||
} else if tcx.lazy_normalization() {
|
||||
// HACK(eddyb) this provides the correct generics when
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![feature(const_generics)]
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
|
@ -0,0 +1,18 @@
|
||||
error: constant expression depends on a generic parameter
|
||||
--> $DIR/complex-generic-default-expr.rs:6:34
|
||||
|
|
||||
LL | struct Foo<const N: usize, const M: usize = { N + 1 }>;
|
||||
| ^
|
||||
|
|
||||
= note: this may fail depending on what value the parameter takes
|
||||
|
||||
error: constant expression depends on a generic parameter
|
||||
--> $DIR/complex-generic-default-expr.rs:10:21
|
||||
|
|
||||
LL | struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: this may fail depending on what value the parameter takes
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -0,0 +1,20 @@
|
||||
error: generic parameters may not be used in const operations
|
||||
--> $DIR/complex-generic-default-expr.rs:6:47
|
||||
|
|
||||
LL | struct Foo<const N: usize, const M: usize = { N + 1 }>;
|
||||
| ^ cannot perform const operation using `N`
|
||||
|
|
||||
= help: const parameters may only be used as standalone arguments, i.e. `N`
|
||||
= help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
|
||||
|
||||
error: generic parameters may not be used in const operations
|
||||
--> $DIR/complex-generic-default-expr.rs:10:62
|
||||
|
|
||||
LL | struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
|
||||
| ^ cannot perform const operation using `T`
|
||||
|
|
||||
= note: type parameters may not be used in const expressions
|
||||
= help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -0,0 +1,14 @@
|
||||
// revisions: full min
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct Foo<const N: usize, const M: usize = { N + 1 }>;
|
||||
//[full]~^ ERROR constant expression depends on a generic parameter
|
||||
//[min]~^^ ERROR generic parameters may not be used in const operations
|
||||
|
||||
struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
|
||||
//[full]~^ ERROR constant expression depends on a generic parameter
|
||||
//[min]~^^ ERROR generic parameters may not be used in const operations
|
||||
|
||||
fn main() {}
|
@ -1,6 +1,6 @@
|
||||
// run-pass
|
||||
|
||||
#![feature(const_generics)]
|
||||
// revisions: full min
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
// run-pass
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
struct Foo<const N: usize, const M: usize = N>([u8; N], [u8; M]);
|
||||
|
||||
fn foo<const N: usize>() -> Foo<N> {
|
||||
let x = [0; N];
|
||||
Foo(x, x)
|
||||
}
|
||||
|
||||
// To check that we actually apply the correct substs for const param defaults.
|
||||
fn concrete_foo() -> Foo<13> {
|
||||
Foo(Default::default(), Default::default())
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let val = foo::<13>();
|
||||
assert_eq!(val.0, val.1);
|
||||
|
||||
let val = concrete_foo();
|
||||
assert_eq!(val.0, val.1);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
// run-pass
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
struct Foo<const N: usize, T = [u8; N]>(T);
|
||||
|
||||
impl<const N: usize> Foo<N> {
|
||||
fn new() -> Self {
|
||||
Foo([0; N])
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(Foo::new().0, [0; 10]);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
|
||||
--> $DIR/default-on-impl.rs:8:12
|
||||
|
|
||||
LL | impl<const N: usize = 1> Foo<N> {}
|
||||
| ^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,8 @@
|
||||
error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
|
||||
--> $DIR/default-on-impl.rs:8:12
|
||||
|
|
||||
LL | impl<const N: usize = 1> Foo<N> {}
|
||||
| ^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
11
src/test/ui/const-generics/defaults/default-on-impl.rs
Normal file
11
src/test/ui/const-generics/defaults/default-on-impl.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// revisions: full min
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct Foo<const N: usize>;
|
||||
|
||||
impl<const N: usize = 1> Foo<N> {}
|
||||
//~^ ERROR defaults for const parameters are only allowed
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,5 @@
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
struct Foo<const N: u8 = { 255 + 1 }>;
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
fn main() {}
|
@ -0,0 +1,9 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/default-param-wf-concrete.rs:3:28
|
||||
|
|
||||
LL | struct Foo<const N: u8 = { 255 + 1 }>;
|
||||
| ^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
@ -1,5 +1,7 @@
|
||||
// aux-build:const_defaulty.rs
|
||||
// check-pass
|
||||
// revisions: full min
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: lifetime parameters must be declared prior to const parameters
|
||||
--> $DIR/intermixed-lifetime.rs:6:28
|
||||
--> $DIR/intermixed-lifetime.rs:7:28
|
||||
|
|
||||
LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
|
||||
| -----------------^^---------- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T = u32>`
|
||||
|
@ -1,26 +1,14 @@
|
||||
error: lifetime parameters must be declared prior to const parameters
|
||||
--> $DIR/intermixed-lifetime.rs:6:28
|
||||
--> $DIR/intermixed-lifetime.rs:7:28
|
||||
|
|
||||
LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
|
||||
| -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
|
||||
| -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const N: usize, T = u32>`
|
||||
|
||||
error: type parameters must be declared prior to const parameters
|
||||
--> $DIR/intermixed-lifetime.rs:6:32
|
||||
|
|
||||
LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
|
||||
| ---------------------^------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
|
||||
|
||||
error: lifetime parameters must be declared prior to const parameters
|
||||
error: lifetime parameters must be declared prior to type parameters
|
||||
--> $DIR/intermixed-lifetime.rs:10:37
|
||||
|
|
||||
LL | struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
|
||||
| --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
|
||||
| --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const N: usize, T = u32>`
|
||||
|
||||
error: type parameters must be declared prior to const parameters
|
||||
--> $DIR/intermixed-lifetime.rs:10:28
|
||||
|
|
||||
LL | struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
|
||||
| -----------------^----------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -1,15 +1,13 @@
|
||||
// revisions: full min
|
||||
// Checks that lifetimes cannot be interspersed between consts and types.
|
||||
// revisions: full min
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![cfg_attr(full, allow(incomplete_features))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
|
||||
//~^ Error lifetime parameters must be declared prior to const parameters
|
||||
//[min]~^^ Error type parameters must be declared prior to const parameters
|
||||
|
||||
struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
|
||||
//[full]~^ Error lifetime parameters must be declared prior to type parameters
|
||||
//[min]~^^ Error type parameters must be declared prior to const parameters
|
||||
//[min]~| Error lifetime parameters must be declared prior to const parameters
|
||||
//~^ Error lifetime parameters must be declared prior to type parameters
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:11:28
|
||||
--> $DIR/mismatch.rs:12:28
|
||||
|
|
||||
LL | let e: Example::<13> = ();
|
||||
| ------------- ^^ expected struct `Example`, found `()`
|
||||
@ -7,7 +7,7 @@ LL | let e: Example::<13> = ();
|
||||
| expected due to this
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:13:34
|
||||
--> $DIR/mismatch.rs:14:34
|
||||
|
|
||||
LL | let e: Example2::<u32, 13> = ();
|
||||
| ------------------- ^^ expected struct `Example2`, found `()`
|
||||
@ -18,7 +18,7 @@ LL | let e: Example2::<u32, 13> = ();
|
||||
found unit type `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:15:34
|
||||
--> $DIR/mismatch.rs:16:34
|
||||
|
|
||||
LL | let e: Example3::<13, u32> = ();
|
||||
| ------------------- ^^ expected struct `Example3`, found `()`
|
||||
@ -29,7 +29,7 @@ LL | let e: Example3::<13, u32> = ();
|
||||
found unit type `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:17:28
|
||||
--> $DIR/mismatch.rs:18:28
|
||||
|
|
||||
LL | let e: Example3::<7> = ();
|
||||
| ------------- ^^ expected struct `Example3`, found `()`
|
||||
@ -40,7 +40,7 @@ LL | let e: Example3::<7> = ();
|
||||
found unit type `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:21:28
|
||||
--> $DIR/mismatch.rs:22:28
|
||||
|
|
||||
LL | let e: Example4::<7> = ();
|
||||
| ------------- ^^ expected struct `Example4`, found `()`
|
52
src/test/ui/const-generics/defaults/mismatch.min.stderr
Normal file
52
src/test/ui/const-generics/defaults/mismatch.min.stderr
Normal file
@ -0,0 +1,52 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:12:28
|
||||
|
|
||||
LL | let e: Example::<13> = ();
|
||||
| ------------- ^^ expected struct `Example`, found `()`
|
||||
| |
|
||||
| expected due to this
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:14:34
|
||||
|
|
||||
LL | let e: Example2::<u32, 13> = ();
|
||||
| ------------------- ^^ expected struct `Example2`, found `()`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected struct `Example2`
|
||||
found unit type `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:16:34
|
||||
|
|
||||
LL | let e: Example3::<13, u32> = ();
|
||||
| ------------------- ^^ expected struct `Example3`, found `()`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected struct `Example3`
|
||||
found unit type `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:18:28
|
||||
|
|
||||
LL | let e: Example3::<7> = ();
|
||||
| ------------- ^^ expected struct `Example3`, found `()`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected struct `Example3<7_usize>`
|
||||
found unit type `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/mismatch.rs:22:28
|
||||
|
|
||||
LL | let e: Example4::<7> = ();
|
||||
| ------------- ^^ expected struct `Example4`, found `()`
|
||||
| |
|
||||
| expected due to this
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -1,4 +1,5 @@
|
||||
#![feature(const_generics)]
|
||||
// revisions: full min
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
|
@ -10,4 +10,4 @@ trait Foo<const KIND: bool = true> {}
|
||||
|
||||
fn foo<const SIZE: usize = 5>() {}
|
||||
|
||||
struct Range<const FROM: usize = 0, const LEN: usize = 0, const TO: usize = {FROM + LEN}>;
|
||||
struct Range<const FROM: usize = 0, const LEN: usize = 0, const TO: usize = FROM>;
|
||||
|
@ -17,4 +17,4 @@ trait Foo<const KIND : bool = true> { }
|
||||
fn foo<const SIZE : usize = 5>() { }
|
||||
|
||||
struct Range<const FROM : usize = 0, const LEN : usize = 0, const TO : usize =
|
||||
{ FROM + LEN }>;
|
||||
FROM>;
|
||||
|
@ -6,7 +6,7 @@
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Loaf<T: Sized, const N: usize = 1usize> {
|
||||
pub struct Loaf<T: Sized, const N: usize = 1> {
|
||||
head: [T; N],
|
||||
slice: [T],
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
error: type parameters must be declared prior to const parameters
|
||||
--> $DIR/simple-defaults.rs:8:40
|
||||
|
|
||||
LL | struct FixedOutput<'a, const N: usize, T=u32> {
|
||||
| ---------------------^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T = u32, const N: usize>`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,12 +1,12 @@
|
||||
// [full] run-pass
|
||||
// revisions: min full
|
||||
// Checks some basic test cases for defaults.
|
||||
// run-pass
|
||||
// Checks that type param defaults are allowed after const params.
|
||||
// revisions: full min
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![cfg_attr(full, allow(incomplete_features))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
struct FixedOutput<'a, const N: usize, T=u32> {
|
||||
//[min]~^ ERROR type parameters must be declared prior to const parameters
|
||||
out: &'a [T; N],
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
// check-pass
|
||||
// revisions: full min
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct N;
|
||||
|
||||
struct Foo<const N: usize = 1, T = N>(T);
|
||||
|
||||
impl Foo {
|
||||
fn new() -> Self {
|
||||
Foo(N)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let Foo::<1, N>(N) = Foo::new();
|
||||
}
|
@ -1,19 +1,8 @@
|
||||
error: generic parameters with a default must be trailing
|
||||
--> $DIR/wrong-order.rs:4:10
|
||||
--> $DIR/wrong-order.rs:6:10
|
||||
|
|
||||
LL | struct A<T = u32, const N: usize> {
|
||||
| ^
|
||||
|
|
||||
= note: using type defaults and const parameters in the same parameter list is currently not permitted
|
||||
|
||||
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/wrong-order.rs:2:27
|
||||
|
|
||||
LL | #![cfg_attr(full, feature(const_generics))]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
error: generic parameters with a default must be trailing
|
||||
--> $DIR/wrong-order.rs:4:10
|
||||
--> $DIR/wrong-order.rs:6:10
|
||||
|
|
||||
LL | struct A<T = u32, const N: usize> {
|
||||
| ^
|
||||
|
|
||||
= note: using type defaults and const parameters in the same parameter list is currently not permitted
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
// revisions: full min
|
||||
#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![feature(const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct A<T = u32, const N: usize> {
|
||||
//~^ ERROR generic parameters with a default must be trailing
|
||||
|
@ -1,22 +1,17 @@
|
||||
error: generic parameters with a default must be trailing
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:12
|
||||
|
|
||||
LL | struct Bar<T = [u8; N], const N: usize>(T);
|
||||
| ^
|
||||
|
|
||||
= note: using type defaults and const parameters in the same parameter list is currently not permitted
|
||||
|
||||
error: constant values inside of type parameter defaults must not depend on generic parameters
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44
|
||||
|
|
||||
LL | struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
|
||||
| ^ the anonymous constant must not depend on the parameter `T`
|
||||
|
||||
error: constant values inside of type parameter defaults must not depend on generic parameters
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21
|
||||
error[E0128]: generic parameters with a default cannot use forward declared identifiers
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21
|
||||
|
|
||||
LL | struct Bar<T = [u8; N], const N: usize>(T);
|
||||
| ^ the anonymous constant must not depend on the parameter `N`
|
||||
| ^ defaulted generic parameters cannot be forward declared
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0128`.
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: generic parameters with a default must be trailing
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:12
|
||||
|
|
||||
LL | struct Bar<T = [u8; N], const N: usize>(T);
|
||||
| ^
|
||||
@ -7,7 +7,7 @@ LL | struct Bar<T = [u8; N], const N: usize>(T);
|
||||
= note: using type defaults and const parameters in the same parameter list is currently not permitted
|
||||
|
||||
error: generic parameters may not be used in const operations
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:6:44
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:5:44
|
||||
|
|
||||
LL | struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
|
||||
| ^ cannot perform const operation using `T`
|
||||
@ -15,11 +15,12 @@ LL | struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
|
||||
= note: type parameters may not be used in const expressions
|
||||
= help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
|
||||
|
||||
error: constant values inside of type parameter defaults must not depend on generic parameters
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:21
|
||||
error[E0128]: generic parameters with a default cannot use forward declared identifiers
|
||||
--> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21
|
||||
|
|
||||
LL | struct Bar<T = [u8; N], const N: usize>(T);
|
||||
| ^ the anonymous constant must not depend on the parameter `N`
|
||||
| ^ defaulted generic parameters cannot be forward declared
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0128`.
|
||||
|
@ -1,15 +1,12 @@
|
||||
// revisions: full min
|
||||
|
||||
#![cfg_attr(full, feature(const_generics))]
|
||||
#![cfg_attr(full, allow(incomplete_features))]
|
||||
|
||||
struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U);
|
||||
//[full]~^ ERROR constant values inside of type parameter defaults
|
||||
//[min]~^^ ERROR generic parameters may not be used in const operations
|
||||
//[min]~^ ERROR generic parameters may not be used in const operations
|
||||
|
||||
// FIXME(const_generics_defaults): We still don't know how to deal with type defaults.
|
||||
struct Bar<T = [u8; N], const N: usize>(T);
|
||||
//~^ ERROR constant values inside of type parameter defaults
|
||||
//~^ ERROR generic parameters with a default cannot use forward declared identifiers
|
||||
//~| ERROR generic parameters with a default
|
||||
|
||||
fn main() {}
|
||||
|
@ -5,5 +5,6 @@ struct Vec<A = Heap, T>(A, T);
|
||||
|
||||
struct Foo<A, B = Vec<C>, C>(A, B, C);
|
||||
//~^ ERROR generic parameters with a default must be trailing
|
||||
//~| ERROR generic parameters with a default cannot use
|
||||
|
||||
fn main() {}
|
||||
|
@ -10,5 +10,12 @@ error: generic parameters with a default must be trailing
|
||||
LL | struct Foo<A, B = Vec<C>, C>(A, B, C);
|
||||
| ^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error[E0128]: generic parameters with a default cannot use forward declared identifiers
|
||||
--> $DIR/generic-non-trailing-defaults.rs:6:23
|
||||
|
|
||||
LL | struct Foo<A, B = Vec<C>, C>(A, B, C);
|
||||
| ^ defaulted generic parameters cannot be forward declared
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0128`.
|
||||
|
Loading…
Reference in New Issue
Block a user