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};
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, and literals.
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}
80
81pub(crate) fn self_ref() -> Ty {
82    Ref(Box::new(Self_), ast::Mutability::Not)
83}
84
85impl Ty {
86    pub(crate) fn to_ty(
87        &self,
88        cx: &ExtCtxt<'_>,
89        span: Span,
90        self_ty: Ident,
91        self_generics: &Generics,
92    ) -> Box<ast::Ty> {
93        match self {
94            Ref(ty, mutbl) => {
95                let raw_ty = ty.to_ty(cx, span, self_ty, self_generics);
96                cx.ty_ref(span, raw_ty, None, *mutbl)
97            }
98            Path(p) => p.to_ty(cx, span, self_ty, self_generics),
99            Self_ => cx.ty_path(self.to_path(cx, span, self_ty, self_generics)),
100            Unit => {
101                let ty = ast::TyKind::Tup(ThinVec::new());
102                cx.ty(span, ty)
103            }
104        }
105    }
106
107    pub(crate) fn to_path(
108        &self,
109        cx: &ExtCtxt<'_>,
110        span: Span,
111        self_ty: Ident,
112        generics: &Generics,
113    ) -> ast::Path {
114        match self {
115            Self_ => {
116                let params: Vec<_> = generics
117                    .params
118                    .iter()
119                    .map(|param| match param.kind {
120                        GenericParamKind::Lifetime { .. } => {
121                            GenericArg::Lifetime(ast::Lifetime { id: param.id, ident: param.ident })
122                        }
123                        GenericParamKind::Type { .. } => {
124                            GenericArg::Type(cx.ty_ident(span, param.ident))
125                        }
126                        GenericParamKind::Const { .. } => {
127                            GenericArg::Const(cx.const_ident(span, param.ident))
128                        }
129                    })
130                    .collect();
131
132                cx.path_all(span, false, vec![self_ty], params)
133            }
134            Path(p) => p.to_path(cx, span, self_ty, generics),
135            Ref(..) => cx.dcx().span_bug(span, "ref in a path in generic `derive`"),
136            Unit => cx.dcx().span_bug(span, "unit in a path in generic `derive`"),
137        }
138    }
139}
140
141fn mk_ty_param(
142    cx: &ExtCtxt<'_>,
143    span: Span,
144    name: Symbol,
145    bounds: &[Path],
146    self_ident: Ident,
147    self_generics: &Generics,
148) -> ast::GenericParam {
149    let bounds = bounds
150        .iter()
151        .map(|b| {
152            let path = b.to_path(cx, span, self_ident, self_generics);
153            cx.trait_bound(path, false)
154        })
155        .collect();
156    cx.typaram(span, Ident::new(name, span), bounds, None)
157}
158
159/// Bounds on type parameters.
160#[derive(Clone)]
161pub(crate) struct Bounds {
162    pub bounds: Vec<(Symbol, Vec<Path>)>,
163}
164
165impl Bounds {
166    pub(crate) fn empty() -> Bounds {
167        Bounds { bounds: Vec::new() }
168    }
169    pub(crate) fn to_generics(
170        &self,
171        cx: &ExtCtxt<'_>,
172        span: Span,
173        self_ty: Ident,
174        self_generics: &Generics,
175    ) -> Generics {
176        let params = self
177            .bounds
178            .iter()
179            .map(|&(name, ref bounds)| mk_ty_param(cx, span, name, bounds, self_ty, self_generics))
180            .collect();
181
182        Generics {
183            params,
184            where_clause: ast::WhereClause {
185                has_where_token: false,
186                predicates: ThinVec::new(),
187                span,
188            },
189            span,
190        }
191    }
192}
193
194pub(crate) fn get_explicit_self(cx: &ExtCtxt<'_>, span: Span) -> (Box<Expr>, ast::ExplicitSelf) {
195    // This constructs a fresh `self` path.
196    let self_path = cx.expr_self(span);
197    let self_ty = respan(span, SelfKind::Region(None, ast::Mutability::Not));
198    (self_path, self_ty)
199}