rustc_builtin_macros/deriving/generic/
ty.rs

1//! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use
2//! when specifying impls to be derived.
3
4pub(crate) use Ty::*;
5use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind, TyKind};
6use rustc_expand::base::ExtCtxt;
7use rustc_span::source_map::respan;
8use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw};
9use thin_vec::ThinVec;
10
11/// A path, e.g., `::std::option::Option::<i32>` (global). Has support
12/// for type parameters.
13#[derive(Clone)]
14pub(crate) struct Path {
15    path: Vec<Symbol>,
16    params: Vec<Box<Ty>>,
17    kind: PathKind,
18}
19
20#[derive(Clone)]
21pub(crate) enum PathKind {
22    Local,
23    Std,
24}
25
26impl Path {
27    pub(crate) fn new(path: Vec<Symbol>) -> Path {
28        Path::new_(path, Vec::new(), PathKind::Std)
29    }
30    pub(crate) fn new_local(path: Symbol) -> Path {
31        Path::new_(vec![path], Vec::new(), PathKind::Local)
32    }
33    pub(crate) fn new_(path: Vec<Symbol>, params: Vec<Box<Ty>>, kind: PathKind) -> Path {
34        Path { path, params, kind }
35    }
36
37    pub(crate) fn to_ty(
38        &self,
39        cx: &ExtCtxt<'_>,
40        span: Span,
41        self_ty: Ident,
42        self_generics: &Generics,
43    ) -> Box<ast::Ty> {
44        cx.ty_path(self.to_path(cx, span, self_ty, self_generics))
45    }
46    pub(crate) fn to_path(
47        &self,
48        cx: &ExtCtxt<'_>,
49        span: Span,
50        self_ty: Ident,
51        self_generics: &Generics,
52    ) -> ast::Path {
53        let mut idents = self.path.iter().map(|s| Ident::new(*s, span)).collect();
54        let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics));
55        let params = tys.map(GenericArg::Type).collect();
56
57        match self.kind {
58            PathKind::Local => cx.path_all(span, false, idents, params),
59            PathKind::Std => {
60                let def_site = cx.with_def_site_ctxt(DUMMY_SP);
61                idents.insert(0, Ident::new(kw::DollarCrate, def_site));
62                cx.path_all(span, false, idents, params)
63            }
64        }
65    }
66}
67
68/// A type. Supports pointers, Self, literals, unit or an arbitrary AST path.
69#[derive(Clone)]
70pub(crate) enum Ty {
71    Self_,
72    /// A reference.
73    Ref(Box<Ty>, ast::Mutability),
74    /// `mod::mod::Type<[lifetime], [Params...]>`, including a plain type
75    /// parameter, and things like `i32`
76    Path(Path),
77    /// For () return types.
78    Unit,
79    /// An arbitrary type.
80    AstTy(Box<ast::Ty>),
81}
82
83pub(crate) fn self_ref() -> Ty {
84    Ref(Box::new(Self_), ast::Mutability::Not)
85}
86
87impl Ty {
88    pub(crate) fn to_ty(
89        &self,
90        cx: &ExtCtxt<'_>,
91        span: Span,
92        self_ty: Ident,
93        self_generics: &Generics,
94    ) -> Box<ast::Ty> {
95        match self {
96            Ref(ty, mutbl) => {
97                let raw_ty = ty.to_ty(cx, span, self_ty, self_generics);
98                cx.ty_ref(span, raw_ty, None, *mutbl)
99            }
100            Path(p) => p.to_ty(cx, span, self_ty, self_generics),
101            Self_ => cx.ty_path(self.to_path(cx, span, self_ty, self_generics)),
102            Unit => {
103                let ty = ast::TyKind::Tup(ThinVec::new());
104                cx.ty(span, ty)
105            }
106            AstTy(ty) => ty.clone(),
107        }
108    }
109
110    pub(crate) fn to_path(
111        &self,
112        cx: &ExtCtxt<'_>,
113        span: Span,
114        self_ty: Ident,
115        generics: &Generics,
116    ) -> ast::Path {
117        match self {
118            Self_ => {
119                let params: Vec<_> = generics
120                    .params
121                    .iter()
122                    .map(|param| match param.kind {
123                        GenericParamKind::Lifetime { .. } => {
124                            GenericArg::Lifetime(ast::Lifetime { id: param.id, ident: param.ident })
125                        }
126                        GenericParamKind::Type { .. } => {
127                            GenericArg::Type(cx.ty_ident(span, param.ident))
128                        }
129                        GenericParamKind::Const { .. } => {
130                            GenericArg::Const(cx.const_ident(span, param.ident))
131                        }
132                    })
133                    .collect();
134
135                cx.path_all(span, false, vec![self_ty], params)
136            }
137            Path(p) => p.to_path(cx, span, self_ty, generics),
138            AstTy(ty) => match &ty.kind {
139                TyKind::Path(_, path) => path.clone(),
140                _ => cx.dcx().span_bug(span, "non-path in a path in generic `derive`"),
141            },
142            Ref(..) => cx.dcx().span_bug(span, "ref in a path in generic `derive`"),
143            Unit => cx.dcx().span_bug(span, "unit in a path in generic `derive`"),
144        }
145    }
146}
147
148fn mk_ty_param(
149    cx: &ExtCtxt<'_>,
150    span: Span,
151    name: Symbol,
152    bounds: &[Path],
153    self_ident: Ident,
154    self_generics: &Generics,
155) -> ast::GenericParam {
156    let bounds = bounds
157        .iter()
158        .map(|b| {
159            let path = b.to_path(cx, span, self_ident, self_generics);
160            cx.trait_bound(path, false)
161        })
162        .collect();
163    cx.typaram(span, Ident::new(name, span), bounds, None)
164}
165
166/// Bounds on type parameters.
167#[derive(Clone)]
168pub(crate) struct Bounds {
169    pub bounds: Vec<(Symbol, Vec<Path>)>,
170}
171
172impl Bounds {
173    pub(crate) fn empty() -> Bounds {
174        Bounds { bounds: Vec::new() }
175    }
176    pub(crate) fn to_generics(
177        &self,
178        cx: &ExtCtxt<'_>,
179        span: Span,
180        self_ty: Ident,
181        self_generics: &Generics,
182    ) -> Generics {
183        let params = self
184            .bounds
185            .iter()
186            .map(|&(name, ref bounds)| mk_ty_param(cx, span, name, bounds, self_ty, self_generics))
187            .collect();
188
189        Generics {
190            params,
191            where_clause: ast::WhereClause {
192                has_where_token: false,
193                predicates: ThinVec::new(),
194                span,
195            },
196            span,
197        }
198    }
199}
200
201pub(crate) fn get_explicit_self(cx: &ExtCtxt<'_>, span: Span) -> (Box<Expr>, ast::ExplicitSelf) {
202    // This constructs a fresh `self` path.
203    let self_path = cx.expr_self(span);
204    let self_ty = respan(span, SelfKind::Region(None, ast::Mutability::Not));
205    (self_path, self_ty)
206}