Add union types

This commit is contained in:
Vadim Petrochenkov 2016-08-07 01:09:00 +03:00
parent 35d52a003b
commit cbd912baba
29 changed files with 124 additions and 69 deletions

View File

@ -168,6 +168,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
ty::TyFnPtr(_) |
ty::TyTrait(..) |
ty::TyStruct(..) |
ty::TyUnion(..) |
ty::TyClosure(..) |
ty::TyNever |
ty::TyTuple(..) |

View File

@ -261,7 +261,8 @@ fn ty_is_local_constructor(tcx: TyCtxt, ty: Ty, infer_is_local: InferIsLocal)->
}
ty::TyEnum(def, _) |
ty::TyStruct(def, _) => {
ty::TyStruct(def, _) |
ty::TyUnion(def, _) => {
def.did.is_local()
}

View File

@ -166,6 +166,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
ty::TyParam(..) => Some(14),
ty::TyAnon(..) => Some(15),
ty::TyNever => Some(16),
ty::TyUnion(..) => Some(17),
ty::TyInfer(..) | ty::TyError => None
}
}

View File

@ -1780,7 +1780,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
Where(ty::Binder(tys.last().into_iter().cloned().collect()))
}
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
ty::TyStruct(def, substs) | ty::TyUnion(def, substs) |
ty::TyEnum(def, substs) => {
let sized_crit = def.sized_constraint(self.tcx());
// (*) binder moved here
Where(ty::Binder(match sized_crit.sty {
@ -1836,7 +1837,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
Where(ty::Binder(tys.to_vec()))
}
ty::TyStruct(..) | ty::TyEnum(..) |
ty::TyStruct(..) | ty::TyUnion(..) | ty::TyEnum(..) |
ty::TyProjection(..) | ty::TyParam(..) | ty::TyAnon(..) => {
// Fallback to whatever user-defined impls exist in this case.
None
@ -1933,7 +1934,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
substs.types().collect()
}
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
ty::TyStruct(def, substs) | ty::TyUnion(def, substs) | ty::TyEnum(def, substs) => {
def.all_fields()
.map(|f| f.ty(self.tcx(), substs))
.collect()

View File

@ -224,7 +224,8 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
|ty| tc_ty(tcx, *ty, cache))
}
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
ty::TyStruct(def, substs) | ty::TyUnion(def, substs) |
ty::TyEnum(def, substs) => {
let mut res =
TypeContents::union(&def.variants, |v| {
TypeContents::union(&v.fields, |f| {

View File

@ -1032,8 +1032,8 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
pub fn print_debug_stats(self) {
sty_debug_print!(
self,
TyEnum, TyBox, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr,
TyTrait, TyStruct, TyClosure, TyTuple, TyParam, TyInfer, TyProjection, TyAnon);
TyEnum, TyBox, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, TyTrait,
TyStruct, TyUnion, TyClosure, TyTuple, TyParam, TyInfer, TyProjection, TyAnon);
println!("Substs interner: #{}", self.interners.substs.borrow().len());
println!("BareFnTy interner: #{}", self.interners.bare_fn.borrow().len());

View File

@ -247,6 +247,9 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
ty::TyStruct(def, _) => {
format!("struct `{}`", tcx.item_path_str(def.did))
}
ty::TyUnion(def, _) => {
format!("union `{}`", tcx.item_path_str(def.did))
}
ty::TyClosure(..) => "closure".to_string(),
ty::TyTuple(_) => "tuple".to_string(),
ty::TyInfer(ty::TyVar(_)) => "inferred type".to_string(),

View File

@ -66,6 +66,9 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
ty::TyStruct(def, _) => {
Some(StructSimplifiedType(def.did))
}
ty::TyUnion(..) => {
unimplemented_unions!();
}
ty::TyRef(_, mt) => {
// since we introduce auto-refs during method lookup, we
// just treat &T and T as equivalent from the point of

View File

@ -102,7 +102,7 @@ impl FlagComputation {
}
}
&ty::TyEnum(_, substs) | &ty::TyStruct(_, substs) => {
&ty::TyEnum(_, substs) | &ty::TyStruct(_, substs) | &ty::TyUnion(_, substs) => {
self.add_substs(substs);
}

View File

@ -320,6 +320,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
pub fn characteristic_def_id_of_type(ty: Ty) -> Option<DefId> {
match ty.sty {
ty::TyStruct(adt_def, _) |
ty::TyUnion(adt_def, _) |
ty::TyEnum(adt_def, _) => Some(adt_def.did),
ty::TyTrait(ref data) => Some(data.principal.def_id()),

View File

@ -896,6 +896,9 @@ impl<'a, 'gcx, 'tcx> Layout {
non_zero: Some(def.did) == tcx.lang_items.non_zero()
}
}
ty::TyUnion(..) => {
unimplemented_unions!();
}
ty::TyEnum(def, substs) => {
let hint = *tcx.lookup_repr_hints(def.did).get(0)
.unwrap_or(&attr::ReprAny);

View File

@ -1421,6 +1421,7 @@ bitflags! {
const IS_PHANTOM_DATA = 1 << 3,
const IS_SIMD = 1 << 4,
const IS_FUNDAMENTAL = 1 << 5,
const IS_UNION = 1 << 7,
}
}
@ -1818,7 +1819,7 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
}
}
TyEnum(adt, substs) | TyStruct(adt, substs) => {
TyEnum(adt, substs) | TyStruct(adt, substs) | TyUnion(adt, substs) => {
// recursive case
let adt = tcx.lookup_adt_def_master(adt.did);
adt.calculate_sized_constraint_inner(tcx, stack);

View File

@ -174,6 +174,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
ty::TyNever | // ...
ty::TyEnum(..) | // OutlivesNominalType
ty::TyStruct(..) | // OutlivesNominalType
ty::TyUnion(..) | // OutlivesNominalType
ty::TyBox(..) | // OutlivesNominalType (ish)
ty::TyAnon(..) | // OutlivesNominalType (ish)
ty::TyStr | // OutlivesScalar (ish)

View File

@ -495,6 +495,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
ty::TyRef(r.fold_with(folder), tm.fold_with(folder))
}
ty::TyStruct(did, substs) => ty::TyStruct(did, substs.fold_with(folder)),
ty::TyUnion(did, substs) => ty::TyUnion(did, substs.fold_with(folder)),
ty::TyClosure(did, substs) => ty::TyClosure(did, substs.fold_with(folder)),
ty::TyProjection(ref data) => ty::TyProjection(data.fold_with(folder)),
ty::TyAnon(did, substs) => ty::TyAnon(did, substs.fold_with(folder)),
@ -524,6 +525,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
ty::TyFnPtr(ref f) => f.visit_with(visitor),
ty::TyRef(r, ref tm) => r.visit_with(visitor) || tm.visit_with(visitor),
ty::TyStruct(_did, ref substs) => substs.visit_with(visitor),
ty::TyUnion(_did, ref substs) => substs.visit_with(visitor),
ty::TyClosure(_did, ref substs) => substs.visit_with(visitor),
ty::TyProjection(ref data) => data.visit_with(visitor),
ty::TyAnon(_, ref substs) => substs.visit_with(visitor),

View File

@ -120,6 +120,11 @@ pub enum TypeVariants<'tcx> {
/// See warning about substitutions for enumerated types.
TyStruct(AdtDef<'tcx>, &'tcx Substs<'tcx>),
/// A union type, defined with `union`.
///
/// See warning about substitutions for enumerated types.
TyUnion(AdtDef<'tcx>, &'tcx Substs<'tcx>),
/// `Box<T>`; this is nominally a struct in the documentation, but is
/// special-cased internally. For example, it is possible to implicitly
/// move the contents of a box out of that box, and methods of any type
@ -1227,6 +1232,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
}
TyEnum(_, substs) |
TyStruct(_, substs) |
TyUnion(_, substs) |
TyAnon(_, substs) => {
substs.regions().collect()
}

View File

@ -430,6 +430,7 @@ impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx> {
TyUint(u) => self.hash(u),
TyFloat(f) => self.hash(f),
TyStruct(d, _) |
TyUnion(d, _) |
TyEnum(d, _) => self.def_id(d.did),
TyArray(_, n) => self.hash(n),
TyRawPtr(m) |
@ -558,7 +559,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
}) => Some(true),
TyArray(..) | TySlice(_) | TyTrait(..) | TyTuple(..) |
TyClosure(..) | TyEnum(..) | TyStruct(..) | TyAnon(..) |
TyClosure(..) | TyEnum(..) | TyStruct(..) | TyUnion(..) | TyAnon(..) |
TyProjection(..) | TyParam(..) | TyInfer(..) | TyError => None
}.unwrap_or_else(|| !self.impls_bound(tcx, param_env, ty::BoundCopy, span));
@ -598,7 +599,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
TyStr | TyTrait(..) | TySlice(_) => Some(false),
TyEnum(..) | TyStruct(..) | TyProjection(..) | TyParam(..) |
TyEnum(..) | TyStruct(..) | TyUnion(..) | TyProjection(..) | TyParam(..) |
TyInfer(..) | TyAnon(..) | TyError => None
}.unwrap_or_else(|| self.impls_bound(tcx, param_env, ty::BoundSized, span));

View File

@ -95,6 +95,7 @@ fn push_subtypes<'tcx>(stack: &mut Vec<Ty<'tcx>>, parent_ty: Ty<'tcx>) {
}
ty::TyEnum(_, ref substs) |
ty::TyStruct(_, ref substs) |
ty::TyUnion(_, ref substs) |
ty::TyAnon(_, ref substs) => {
stack.extend(substs.types().rev());
}

View File

@ -337,7 +337,8 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
}
ty::TyEnum(def, substs) |
ty::TyStruct(def, substs) => {
ty::TyStruct(def, substs) |
ty::TyUnion(def, substs) => {
// WfNominalType
let obligations = self.nominal_obligations(def.did, substs);
self.out.extend(obligations);

View File

@ -8,11 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use hir::def_id::DefId;
use ty::subst::{self, Subst, Substs};
use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
use ty::{TyBool, TyChar, TyStruct, TyEnum};
use ty::{TyBool, TyChar, TyStruct, TyUnion, TyEnum};
use ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr};
use ty::{TyParam, TyRawPtr, TyRef, TyNever, TyTuple};
use ty::TyClosure;
@ -869,7 +868,7 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
TyInfer(infer_ty) => write!(f, "{}", infer_ty),
TyError => write!(f, "[type error]"),
TyParam(ref param_ty) => write!(f, "{}", param_ty),
TyEnum(def, substs) | TyStruct(def, substs) => {
TyEnum(def, substs) | TyStruct(def, substs) | TyUnion(def, substs) => {
ty::tls::with(|tcx| {
if def.did.is_local() &&
!tcx.tcache.borrow().contains_key(&def.did) {

View File

@ -377,7 +377,8 @@ enum FfiResult {
FfiSafe,
FfiUnsafe(&'static str),
FfiBadStruct(DefId, &'static str),
FfiBadEnum(DefId, &'static str)
FfiBadUnion(DefId, &'static str),
FfiBadEnum(DefId, &'static str),
}
/// Check if this enum can be safely exported based on the
@ -452,12 +453,32 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
let r = self.check_type_for_ffi(cache, field_ty);
match r {
FfiSafe => {}
FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; }
FfiUnsafe(s) => { return FfiBadStruct(def.did, s); }
}
}
FfiSafe
}
ty::TyUnion(def, substs) => {
if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
return FfiUnsafe(
"found union without foreign-function-safe \
representation annotation in foreign module, \
consider adding a #[repr(C)] attribute to \
the type");
}
for field in &def.struct_variant().fields {
let field_ty = cx.normalize_associated_type(&field.ty(cx, substs));
let r = self.check_type_for_ffi(cache, field_ty);
match r {
FfiSafe => {}
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; }
FfiUnsafe(s) => { return FfiBadUnion(def.did, s); }
}
}
FfiSafe
}
ty::TyEnum(def, substs) => {
if def.variants.is_empty() {
// Empty enums are okay... although sort of useless.
@ -507,7 +528,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
let r = self.check_type_for_ffi(cache, arg);
match r {
FfiSafe => {}
FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; }
FfiUnsafe(s) => { return FfiBadEnum(def.did, s); }
}
}
@ -614,6 +635,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
&format!("found non-foreign-function-safe member in \
struct marked #[repr(C)]: {}", s));
}
FfiResult::FfiBadUnion(_, s) => {
// FIXME: This diagnostic is difficult to read, and doesn't
// point at the relevant field.
self.cx.span_lint(IMPROPER_CTYPES, sp,
&format!("found non-foreign-function-safe member in \
union marked #[repr(C)]: {}", s));
}
FfiResult::FfiBadEnum(_, s) => {
// FIXME: This diagnostic is difficult to read, and doesn't
// point at the relevant variant.

View File

@ -170,6 +170,11 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx
enc_substs(w, cx, substs);
write!(w, "]");
}
ty::TyUnion(def, substs) => {
write!(w, "u[{}|", (cx.ds)(cx.tcx, def.did));
enc_substs(w, cx, substs);
write!(w, "]");
}
ty::TyClosure(def, substs) => {
write!(w, "k[{}|", (cx.ds)(cx.tcx, def));
enc_substs(w, cx, substs.func_substs);

View File

@ -798,6 +798,7 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
/* nothing to do */
}
ty::TyStruct(ref adt_def, substs) |
ty::TyUnion(ref adt_def, substs) |
ty::TyEnum(ref adt_def, substs) => {
for field in adt_def.all_fields() {
let field_type = monomorphize::apply_param_substs(scx,

View File

@ -45,6 +45,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
ty::TyUint(uint_ty) => output.push_str(uint_ty.ty_to_string()),
ty::TyFloat(float_ty) => output.push_str(float_ty.ty_to_string()),
ty::TyStruct(def, substs) |
ty::TyUnion(def, substs) |
ty::TyEnum(def, substs) => {
push_item_name(cx, def.did, qualified, output);
push_type_params(cx, substs, output);

View File

@ -397,6 +397,7 @@ pub fn push_unique_type_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty::TyFloat(ast::FloatTy::F32) => output.push_str("f32"),
ty::TyFloat(ast::FloatTy::F64) => output.push_str("f64"),
ty::TyStruct(adt_def, substs) |
ty::TyUnion(adt_def, substs) |
ty::TyEnum(adt_def, substs) => {
push_item_name(tcx, adt_def.did, output);
push_type_params(tcx, substs, &[], output);

View File

@ -89,27 +89,23 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
Type::nil(cx)
}
ty::TyTuple(..) | ty::TyEnum(..) | ty::TyClosure(..) => {
let repr = adt::represent_type(cx, t);
adt::sizing_type_of(cx, &repr, false)
ty::TyStruct(..) if t.is_simd() => {
let e = t.simd_type(cx.tcx());
if !e.is_machine() {
cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \
a non-machine element type `{}`",
t, e))
}
let llet = type_of(cx, e);
let n = t.simd_size(cx.tcx()) as u64;
ensure_array_fits_in_address_space(cx, llet, n, t);
Type::vector(&llet, n)
}
ty::TyStruct(..) => {
if t.is_simd() {
let e = t.simd_type(cx.tcx());
if !e.is_machine() {
cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \
a non-machine element type `{}`",
t, e))
}
let llet = type_of(cx, e);
let n = t.simd_size(cx.tcx()) as u64;
ensure_array_fits_in_address_space(cx, llet, n, t);
Type::vector(&llet, n)
} else {
let repr = adt::represent_type(cx, t);
adt::sizing_type_of(cx, &repr, false)
}
ty::TyTuple(..) | ty::TyStruct(..) | ty::TyUnion(..) |
ty::TyEnum(..) | ty::TyClosure(..) => {
let repr = adt::represent_type(cx, t);
adt::sizing_type_of(cx, &repr, false)
}
ty::TyProjection(..) | ty::TyInfer(..) | ty::TyParam(..) |
@ -244,15 +240,6 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
ty::TyUint(t) => Type::uint_from_ty(cx, t),
ty::TyFloat(t) => Type::float_from_ty(cx, t),
ty::TyNever => Type::nil(cx),
ty::TyEnum(def, ref substs) => {
// Only create the named struct, but don't fill it in. We
// fill it in *after* placing it into the type cache. This
// avoids creating more than one copy of the enum when one
// of the enum's variants refers to the enum itself.
let repr = adt::represent_type(cx, t);
let name = llvm_type_name(cx, def.did, substs);
adt::incomplete_type_of(cx, &repr, &name[..])
}
ty::TyClosure(..) => {
// Only create the named struct, but don't fill it in. We
// fill it in *after* placing it into the type cache.
@ -307,26 +294,28 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
let repr = adt::represent_type(cx, t);
adt::type_of(cx, &repr)
}
ty::TyStruct(def, ref substs) => {
if t.is_simd() {
let e = t.simd_type(cx.tcx());
if !e.is_machine() {
cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \
a non-machine element type `{}`",
t, e))
}
let llet = in_memory_type_of(cx, e);
let n = t.simd_size(cx.tcx()) as u64;
ensure_array_fits_in_address_space(cx, llet, n, t);
Type::vector(&llet, n)
} else {
// Only create the named struct, but don't fill it in. We fill it
// in *after* placing it into the type cache. This prevents
// infinite recursion with recursive struct types.
let repr = adt::represent_type(cx, t);
let name = llvm_type_name(cx, def.did, substs);
adt::incomplete_type_of(cx, &repr, &name[..])
ty::TyStruct(..) if t.is_simd() => {
let e = t.simd_type(cx.tcx());
if !e.is_machine() {
cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \
a non-machine element type `{}`",
t, e))
}
let llet = in_memory_type_of(cx, e);
let n = t.simd_size(cx.tcx()) as u64;
ensure_array_fits_in_address_space(cx, llet, n, t);
Type::vector(&llet, n)
}
ty::TyStruct(def, ref substs) |
ty::TyUnion(def, ref substs) |
ty::TyEnum(def, ref substs) => {
// Only create the named struct, but don't fill it in. We
// fill it in *after* placing it into the type cache. This
// avoids creating more than one copy of the enum when one
// of the enum's variants refers to the enum itself.
let repr = adt::represent_type(cx, t);
let name = llvm_type_name(cx, def.did, substs);
adt::incomplete_type_of(cx, &repr, &name[..])
}
ty::TyInfer(..) |

View File

@ -439,7 +439,7 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
cx, context, ity, depth+1)
}
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
ty::TyStruct(def, substs) | ty::TyUnion(def, substs) | ty::TyEnum(def, substs) => {
let did = def.did;
for variant in &def.variants {
for field in variant.fields.iter() {

View File

@ -24,7 +24,7 @@ use rustc::ty::{ImplOrTraitItemId, ConstTraitItemId};
use rustc::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment};
use rustc::ty::{Ty, TyBool, TyChar, TyEnum, TyError};
use rustc::ty::{TyParam, TyRawPtr};
use rustc::ty::{TyRef, TyStruct, TyTrait, TyNever, TyTuple};
use rustc::ty::{TyRef, TyStruct, TyUnion, TyTrait, TyNever, TyTuple};
use rustc::ty::{TyStr, TyArray, TySlice, TyFloat, TyInfer, TyInt};
use rustc::ty::{TyUint, TyClosure, TyBox, TyFnDef, TyFnPtr};
use rustc::ty::{TyProjection, TyAnon};
@ -70,7 +70,8 @@ impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> {
fn get_base_type_def_id(&self, span: Span, ty: Ty<'tcx>) -> Option<DefId> {
match ty.sty {
TyEnum(def, _) |
TyStruct(def, _) => {
TyStruct(def, _) |
TyUnion(def, _) => {
Some(def.did)
}

View File

@ -344,7 +344,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
ty::TyEnum(def, substs) |
ty::TyStruct(def, substs) => {
ty::TyStruct(def, substs) |
ty::TyUnion(def, substs) => {
let item_type = self.tcx().lookup_item_type(def.did);
// This edge is actually implied by the call to

View File

@ -1801,6 +1801,7 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
decl: (cx.map.local_def_id(0), &fty.sig).clean(cx),
abi: fty.abi,
}),
ty::TyUnion(..) => unimplemented_unions!(),
ty::TyStruct(def, substs) |
ty::TyEnum(def, substs) => {
let did = def.did;