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