rustc_const_eval: keep track of the appropriate ParamEnv.

This commit is contained in:
Eduard-Mihai Burtescu 2017-07-26 15:51:53 +03:00
parent 4c900c5248
commit 60cf5428b3
15 changed files with 107 additions and 64 deletions

View file

@ -14,7 +14,8 @@ pub use rustc_const_math::ConstInt;
use hir;
use hir::def::Def;
use hir::def_id::DefId;
use ty::{TyCtxt, layout};
use traits::Reveal;
use ty::{self, TyCtxt, layout};
use ty::subst::Substs;
use util::common::ErrorReported;
use rustc_const_math::*;
@ -229,8 +230,9 @@ pub fn eval_length(tcx: TyCtxt,
{
let count_expr = &tcx.hir.body(count).value;
let count_def_id = tcx.hir.body_owner_def_id(count);
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
let substs = Substs::identity_for_item(tcx.global_tcx(), count_def_id);
match tcx.at(count_expr.span).const_eval((count_def_id, substs)) {
match tcx.at(count_expr.span).const_eval(param_env.and((count_def_id, substs))) {
Ok(Integral(Usize(count))) => {
let val = count.as_u64(tcx.sess.target.uint_type);
assert_eq!(val as usize as u64, val);

View file

@ -372,8 +372,8 @@ impl<'tcx> QueryDescription for queries::reachable_set<'tcx> {
}
impl<'tcx> QueryDescription for queries::const_eval<'tcx> {
fn describe(tcx: TyCtxt, (def_id, _): (DefId, &'tcx Substs<'tcx>)) -> String {
format!("const-evaluating `{}`", tcx.item_path_str(def_id))
fn describe(tcx: TyCtxt, key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> String {
format!("const-evaluating `{}`", tcx.item_path_str(key.value.0))
}
}
@ -935,7 +935,7 @@ define_maps! { <'tcx>
/// Results of evaluating const items or constants embedded in
/// other items (such as enum variant explicit discriminants).
[] const_eval: const_eval_dep_node((DefId, &'tcx Substs<'tcx>))
[] const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>)
-> const_val::EvalResult<'tcx>,
/// Performs the privacy check and computes "access levels".
@ -1032,8 +1032,9 @@ fn typeck_item_bodies_dep_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> {
DepConstructor::TypeckBodiesKrate
}
fn const_eval_dep_node<'tcx>((def_id, substs): (DefId, &'tcx Substs<'tcx>))
fn const_eval_dep_node<'tcx>(key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>)
-> DepConstructor<'tcx> {
let (def_id, substs) = key.value;
DepConstructor::ConstEval { def_id, substs }
}

View file

@ -1582,6 +1582,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
#[inline]
pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
-> impl Iterator<Item=ConstInt> + 'a {
let param_env = ParamEnv::empty(traits::Reveal::UserFacing);
let repr_type = self.repr.discr_type();
let initial = repr_type.initial_discriminant(tcx.global_tcx());
let mut prev_discr = None::<ConstInt>;
@ -1589,7 +1590,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr());
if let VariantDiscr::Explicit(expr_did) = v.discr {
let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did);
match tcx.const_eval((expr_did, substs)) {
match tcx.const_eval(param_env.and((expr_did, substs))) {
Ok(ConstVal::Integral(v)) => {
discr = v;
}
@ -1617,6 +1618,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
variant_index: usize)
-> ConstInt {
let param_env = ParamEnv::empty(traits::Reveal::UserFacing);
let repr_type = self.repr.discr_type();
let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx());
let mut explicit_index = variant_index;
@ -1628,7 +1630,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
}
ty::VariantDiscr::Explicit(expr_did) => {
let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did);
match tcx.const_eval((expr_did, substs)) {
match tcx.const_eval(param_env.and((expr_did, substs))) {
Ok(ConstVal::Integral(v)) => {
explicit_value = v;
break;

View file

@ -398,7 +398,7 @@ macro_rules! CopyImpls {
}
}
CopyImpls! { (), hir::Unsafety, abi::Abi }
CopyImpls! { (), hir::Unsafety, abi::Abi, hir::def_id::DefId }
impl<'tcx, T:TypeFoldable<'tcx>, U:TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> (T, U) {

View file

@ -165,8 +165,9 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
arm.pats.iter().map(|pat| {
let substs = self.identity_substs;
let mut patcx = PatternContext::new(self.tcx, self.tables, substs);
let mut patcx = PatternContext::new(self.tcx,
self.param_env.and(self.identity_substs),
self.tables);
let pattern = expand_pattern(cx, patcx.lower_pattern(&pat));
if !patcx.errors.is_empty() {
patcx.report_inlining_errors(pat.span);
@ -233,8 +234,9 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
fn check_irrefutable(&self, pat: &Pat, origin: &str) {
let module = self.tcx.hir.get_module_parent(pat.id);
MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {
let substs = self.identity_substs;
let mut patcx = PatternContext::new(self.tcx, self.tables, substs);
let mut patcx = PatternContext::new(self.tcx,
self.param_env.and(self.identity_substs),
self.tables);
let pattern = patcx.lower_pattern(pat);
let pattern_ty = pattern.ty;
let pats : Matrix = vec![vec![

View file

@ -21,7 +21,6 @@ use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::maps::Providers;
use rustc::ty::util::IntTypeExt;
use rustc::ty::subst::{Substs, Subst};
use rustc::traits::Reveal;
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::DefIdMap;
@ -49,24 +48,21 @@ macro_rules! math {
}
}
/// * `def_id` is the id of the constant.
/// * `substs` is the monomorphized substitutions for the expression.
///
/// `substs` is optional and is used for associated constants.
/// This generally happens in late/trans const evaluation.
/// * `DefId` is the id of the constant.
/// * `Substs` is the monomorphized substitutions for the expression.
pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>)
key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>)
-> Option<(DefId, &'tcx Substs<'tcx>)> {
let (def_id, _) = key.value;
if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
match tcx.hir.find(node_id) {
Some(hir_map::NodeTraitItem(_)) => {
// If we have a trait item and the substitutions for it,
// `resolve_trait_associated_const` will select an impl
// or the default.
resolve_trait_associated_const(tcx, def_id, substs)
resolve_trait_associated_const(tcx, key)
}
_ => Some((def_id, substs))
_ => Some(key.value)
}
} else {
match tcx.describe_def(def_id) {
@ -76,12 +72,12 @@ pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// trait-associated const if the caller gives us the
// substitutions for the reference to it.
if tcx.trait_of_item(def_id).is_some() {
resolve_trait_associated_const(tcx, def_id, substs)
resolve_trait_associated_const(tcx, key)
} else {
Some((def_id, substs))
Some(key.value)
}
}
_ => Some((def_id, substs))
_ => Some(key.value)
}
}
}
@ -89,18 +85,21 @@ pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub struct ConstContext<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
param_env: ty::ParamEnv<'tcx>,
substs: &'tcx Substs<'tcx>,
fn_args: Option<DefIdMap<ConstVal<'tcx>>>
}
impl<'a, 'tcx> ConstContext<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
substs: &'tcx Substs<'tcx>) -> Self {
param_env_and_substs: ty::ParamEnvAnd<'tcx, &'tcx Substs<'tcx>>,
tables: &'a ty::TypeckTables<'tcx>)
-> Self {
ConstContext {
tcx,
param_env: param_env_and_substs.param_env,
tables,
substs,
substs: param_env_and_substs.value,
fn_args: None
}
}
@ -279,7 +278,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
match cx.tables.qpath_def(qpath, e.id) {
Def::Const(def_id) |
Def::AssociatedConst(def_id) => {
match tcx.at(e.span).const_eval((def_id, substs)) {
match tcx.at(e.span).const_eval(cx.param_env.and((def_id, substs))) {
Ok(val) => val,
Err(ConstEvalErr { kind: TypeckError, .. }) => {
signal!(e, TypeckError);
@ -323,10 +322,9 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
if tcx.fn_sig(def_id).abi() == Abi::RustIntrinsic {
let layout_of = |ty: Ty<'tcx>| {
ty.layout(tcx, ty::ParamEnv::empty(traits::Reveal::All))
.map_err(|err| {
ConstEvalErr { span: e.span, kind: LayoutError(err) }
})
ty.layout(tcx, cx.param_env).map_err(|err| {
ConstEvalErr { span: e.span, kind: LayoutError(err) }
})
};
match &tcx.item_name(def_id).as_str()[..] {
"size_of" => {
@ -377,7 +375,8 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
}
debug!("const call({:?})", call_args);
let callee_cx = ConstContext {
tcx: tcx,
tcx,
param_env: cx.param_env,
tables: tcx.typeck_tables_of(def_id),
substs: substs,
fn_args: Some(call_args)
@ -477,9 +476,10 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
}
fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>)
key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>)
-> Option<(DefId, &'tcx Substs<'tcx>)> {
let param_env = key.param_env;
let (def_id, substs) = key.value;
let trait_item = tcx.associated_item(def_id);
let trait_id = trait_item.container.id();
let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs));
@ -487,7 +487,6 @@ fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
trait_ref);
tcx.infer_ctxt().enter(|infcx| {
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
let mut selcx = traits::SelectionContext::new(&infcx);
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
param_env,
@ -506,10 +505,8 @@ fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
};
// NOTE: this code does not currently account for specialization, but when
// it does so, it should hook into the Reveal to determine when the
// constant should resolve; this will also require plumbing through to this
// function whether we are in "trans mode" to pick the right Reveal
// when constructing the inference context above.
// it does so, it should hook into the param_env.reveal to determine when the
// constant should resolve.
match selection {
traits::VtableImpl(ref impl_data) => {
let name = trait_item.name;
@ -524,15 +521,16 @@ fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
None => {
if trait_item.defaultness.has_value() {
Some((def_id, substs))
Some(key.value)
} else {
None
}
}
}
}
traits::VtableParam(_) => None,
_ => {
bug!("resolve_trait_associated_const: unexpected vtable type")
bug!("resolve_trait_associated_const: unexpected vtable type {:?}", selection)
}
}
})
@ -761,13 +759,13 @@ pub fn provide(providers: &mut Providers) {
}
fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
(def_id, substs): (DefId, &'tcx Substs<'tcx>))
key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>)
-> EvalResult<'tcx> {
let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, def_id, substs) {
let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) {
resolved
} else {
return Err(ConstEvalErr {
span: tcx.def_span(def_id),
span: tcx.def_span(key.value.0),
kind: TypeckError
});
};
@ -779,5 +777,5 @@ fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
} else {
tcx.sess.cstore.item_body(tcx, def_id)
};
ConstContext::new(tcx, tables, substs).eval(&body.value)
ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value)
}

View file

@ -268,6 +268,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
pub struct PatternContext<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub param_env: ty::ParamEnv<'tcx>,
pub tables: &'a ty::TypeckTables<'tcx>,
pub substs: &'tcx Substs<'tcx>,
pub errors: Vec<PatternError<'tcx>>,
@ -275,10 +276,10 @@ pub struct PatternContext<'a, 'tcx: 'a> {
impl<'a, 'tcx> Pattern<'tcx> {
pub fn from_hir(tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env_and_substs: ty::ParamEnvAnd<'tcx, &'tcx Substs<'tcx>>,
tables: &'a ty::TypeckTables<'tcx>,
substs: &'tcx Substs<'tcx>,
pat: &hir::Pat) -> Self {
let mut pcx = PatternContext::new(tcx, tables, substs);
let mut pcx = PatternContext::new(tcx, param_env_and_substs, tables);
let result = pcx.lower_pattern(pat);
if !pcx.errors.is_empty() {
span_bug!(pat.span, "encountered errors lowering pattern: {:?}", pcx.errors)
@ -290,9 +291,15 @@ impl<'a, 'tcx> Pattern<'tcx> {
impl<'a, 'tcx> PatternContext<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
substs: &'tcx Substs<'tcx>) -> Self {
PatternContext { tcx, tables, substs, errors: vec![] }
param_env_and_substs: ty::ParamEnvAnd<'tcx, &'tcx Substs<'tcx>>,
tables: &'a ty::TypeckTables<'tcx>) -> Self {
PatternContext {
tcx,
param_env: param_env_and_substs.param_env,
tables,
substs: param_env_and_substs.value,
errors: vec![]
}
}
pub fn lower_pattern(&mut self, pat: &hir::Pat) -> Pattern<'tcx> {
@ -588,7 +595,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
let kind = match def {
Def::Const(def_id) | Def::AssociatedConst(def_id) => {
let substs = self.tables.node_substs(id);
match eval::lookup_const_by_id(self.tcx, def_id, substs) {
match eval::lookup_const_by_id(self.tcx, self.param_env.and((def_id, substs))) {
Some((def_id, substs)) => {
// Enter the inlined constant's tables&substs temporarily.
let old_tables = self.tables;
@ -622,7 +629,9 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
}
fn lower_lit(&mut self, expr: &hir::Expr) -> PatternKind<'tcx> {
let const_cx = eval::ConstContext::new(self.tcx, self.tables, self.substs);
let const_cx = eval::ConstContext::new(self.tcx,
self.param_env.and(self.substs),
self.tables);
match const_cx.eval(expr) {
Ok(value) => {
if let ConstVal::Variant(def_id) = value {

View file

@ -113,7 +113,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
let parent_item = cx.tcx.hir.get_parent(e.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
let const_cx = ConstContext::new(cx.tcx, cx.tables, substs);
let const_cx = ConstContext::new(cx.tcx,
cx.param_env.and(substs),
cx.tables);
match const_cx.eval(&r) {
Ok(ConstVal::Integral(i)) => {
i.is_negative() ||

View file

@ -514,8 +514,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if let Some(pattern) = pattern {
let pattern = Pattern::from_hir(self.hir.tcx().global_tcx(),
self.hir.param_env.and(self.hir.identity_substs),
self.hir.tables(),
self.hir.identity_substs,
pattern);
scope = self.declare_bindings(scope, ast_body.span, &pattern);
unpack!(block = self.lvalue_into_pattern(block, pattern, &lvalue));

View file

@ -71,8 +71,8 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
});
let pattern = Pattern::from_hir(cx.tcx.global_tcx(),
cx.param_env.and(cx.identity_substs),
cx.tables(),
cx.identity_substs,
&local.pat);
result.push(StmtRef::Mirror(Box::new(Stmt {
span: stmt.span,

View file

@ -467,7 +467,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
let c = &cx.tcx.hir.body(count).value;
let def_id = cx.tcx.hir.body_owner_def_id(count);
let substs = Substs::identity_for_item(cx.tcx.global_tcx(), def_id);
let count = match cx.tcx.at(c.span).const_eval((def_id, substs)) {
let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and((def_id, substs))) {
Ok(ConstVal::Integral(ConstInt::Usize(u))) => u,
Ok(other) => bug!("constant evaluation of repeat count yielded {:?}", other),
Err(s) => cx.fatal_const_eval_err(&s, c.span, "expression")
@ -605,7 +605,10 @@ fn to_borrow_kind(m: hir::Mutability) -> BorrowKind {
fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm) -> Arm<'tcx> {
Arm {
patterns: arm.pats.iter().map(|p| {
Pattern::from_hir(cx.tcx.global_tcx(), cx.tables(), cx.identity_substs, p)
Pattern::from_hir(cx.tcx.global_tcx(),
cx.param_env.and(cx.identity_substs),
cx.tables(),
p)
}).collect(),
guard: arm.guard.to_ref(),
body: arm.body.to_ref(),

View file

@ -37,7 +37,7 @@ pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
pub param_env: ty::ParamEnv<'tcx>,
pub param_env: ty::ParamEnv<'gcx>,
/// Identity `Substs` for use with const-evaluation.
pub identity_substs: &'gcx Substs<'gcx>,
@ -135,7 +135,10 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
pub fn const_eval_literal(&mut self, e: &hir::Expr) -> Literal<'tcx> {
let tcx = self.tcx.global_tcx();
match ConstContext::new(tcx, self.tables(), self.identity_substs).eval(e) {
let const_cx = ConstContext::new(tcx,
self.param_env.and(self.identity_substs),
self.tables());
match const_cx.eval(e) {
Ok(value) => Literal::Value { value: value },
Err(s) => self.fatal_const_eval_err(&s, e.span, "expression")
}

View file

@ -65,7 +65,7 @@ struct CheckCrateVisitor<'a, 'tcx: 'a> {
impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
fn const_cx(&self) -> ConstContext<'a, 'gcx> {
ConstContext::new(self.tcx, self.tables, self.identity_substs)
ConstContext::new(self.tcx, self.param_env.and(self.identity_substs), self.tables)
}
fn check_const_eval(&self, expr: &'gcx hir::Expr) {

View file

@ -59,6 +59,7 @@ use constrained_type_params as ctp;
use middle::lang_items::SizedTraitLangItem;
use middle::const_val::ConstVal;
use middle::resolve_lifetime as rl;
use rustc::traits::Reveal;
use rustc::ty::subst::Substs;
use rustc::ty::{ToPredicate, ReprOptions};
use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt};
@ -550,6 +551,7 @@ fn convert_variant_ctor<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
variants: &[hir::Variant]) {
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
let def = tcx.adt_def(def_id);
let repr_type = def.repr.discr_type();
let initial = repr_type.initial_discriminant(tcx);
@ -561,7 +563,7 @@ fn convert_enum_variant_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
prev_discr = Some(if let Some(e) = variant.node.disr_expr {
let expr_did = tcx.hir.local_def_id(e.node_id);
let substs = Substs::identity_for_item(tcx, expr_did);
let result = tcx.at(variant.span).const_eval((expr_did, substs));
let result = tcx.at(variant.span).const_eval(param_env.and((expr_did, substs)));
// enum variant evaluation happens before the global constant check
// so we need to report the real error

View file

@ -0,0 +1,19 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
trait Trait {
type Output;
}
fn f<T: Trait>() {
std::mem::size_of::<T::Output>();
}
fn main() {}