Skip to content

Commit 5b49745

Browse files
committed
Auto merge of rust-lang#13269 - Veykril:repr, r=Veykril
Properly set the enum variant body type from the repr attribute Follow up for rust-lang/rust-analyzer#12966, fixes some type inference problems
2 parents 817a6a8 + 9bf386f commit 5b49745

File tree

8 files changed

+182
-70
lines changed

8 files changed

+182
-70
lines changed

crates/hir-def/src/adt.rs

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Defines hir-level representation of structs, enums and unions
22
3-
use std::sync::Arc;
3+
use std::{num::NonZeroU32, sync::Arc};
44

55
use base_db::CrateId;
66
use either::Either;
@@ -14,6 +14,7 @@ use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
1414

1515
use crate::{
1616
body::{CfgExpander, LowerCtx},
17+
builtin_type::{BuiltinInt, BuiltinUint},
1718
db::DefDatabase,
1819
intern::Interned,
1920
item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem, RawVisibilityId},
@@ -31,14 +32,15 @@ use cfg::CfgOptions;
3132
pub struct StructData {
3233
pub name: Name,
3334
pub variant_data: Arc<VariantData>,
34-
pub repr: Option<ReprKind>,
35+
pub repr: Option<ReprData>,
3536
pub visibility: RawVisibility,
3637
}
3738

3839
#[derive(Debug, Clone, PartialEq, Eq)]
3940
pub struct EnumData {
4041
pub name: Name,
4142
pub variants: Arena<EnumVariantData>,
43+
pub repr: Option<ReprData>,
4244
pub visibility: RawVisibility,
4345
}
4446

@@ -63,32 +65,80 @@ pub struct FieldData {
6365
pub visibility: RawVisibility,
6466
}
6567

66-
#[derive(Debug, Clone, PartialEq, Eq)]
68+
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
6769
pub enum ReprKind {
68-
Packed,
69-
Other,
70+
C,
71+
BuiltinInt { builtin: Either<BuiltinInt, BuiltinUint>, is_c: bool },
72+
Transparent,
73+
Default,
74+
}
75+
76+
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
77+
pub struct ReprData {
78+
pub kind: ReprKind,
79+
pub packed: bool,
80+
pub align: Option<NonZeroU32>,
7081
}
7182

7283
fn repr_from_value(
7384
db: &dyn DefDatabase,
7485
krate: CrateId,
7586
item_tree: &ItemTree,
7687
of: AttrOwner,
77-
) -> Option<ReprKind> {
88+
) -> Option<ReprData> {
7889
item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
7990
}
8091

81-
fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
92+
fn parse_repr_tt(tt: &Subtree) -> Option<ReprData> {
8293
match tt.delimiter {
8394
Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
8495
_ => return None,
8596
}
8697

87-
let mut it = tt.token_trees.iter();
88-
match it.next()? {
89-
TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed),
90-
_ => Some(ReprKind::Other),
98+
let mut data = ReprData { kind: ReprKind::Default, packed: false, align: None };
99+
100+
let mut tts = tt.token_trees.iter().peekable();
101+
while let Some(tt) = tts.next() {
102+
if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
103+
match &*ident.text {
104+
"packed" => {
105+
data.packed = true;
106+
if let Some(TokenTree::Subtree(_)) = tts.peek() {
107+
tts.next();
108+
}
109+
}
110+
"align" => {
111+
if let Some(TokenTree::Subtree(tt)) = tts.peek() {
112+
tts.next();
113+
if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
114+
if let Ok(align) = lit.text.parse() {
115+
data.align = Some(align);
116+
}
117+
}
118+
}
119+
}
120+
"C" => {
121+
if let ReprKind::BuiltinInt { is_c, .. } = &mut data.kind {
122+
*is_c = true;
123+
} else {
124+
data.kind = ReprKind::C;
125+
}
126+
}
127+
"transparent" => data.kind = ReprKind::Transparent,
128+
repr => {
129+
let is_c = matches!(data.kind, ReprKind::C);
130+
if let Some(builtin) = BuiltinInt::from_suffix(repr)
131+
.map(Either::Left)
132+
.or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
133+
{
134+
data.kind = ReprKind::BuiltinInt { builtin, is_c };
135+
}
136+
}
137+
}
138+
}
91139
}
140+
141+
Some(data)
92142
}
93143

94144
impl StructData {
@@ -108,6 +158,7 @@ impl StructData {
108158
visibility: item_tree[strukt.visibility].clone(),
109159
})
110160
}
161+
111162
pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
112163
let loc = id.lookup(db);
113164
let krate = loc.container.krate;
@@ -133,6 +184,7 @@ impl EnumData {
133184
let krate = loc.container.krate;
134185
let item_tree = loc.id.item_tree(db);
135186
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
187+
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
136188

137189
let enum_ = &item_tree[loc.id.value];
138190
let mut variants = Arena::new();
@@ -158,6 +210,7 @@ impl EnumData {
158210
Arc::new(EnumData {
159211
name: enum_.name.clone(),
160212
variants,
213+
repr,
161214
visibility: item_tree[enum_.visibility].clone(),
162215
})
163216
}
@@ -166,6 +219,13 @@ impl EnumData {
166219
let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
167220
Some(id)
168221
}
222+
223+
pub fn variant_body_type(&self) -> Either<BuiltinInt, BuiltinUint> {
224+
match self.repr {
225+
Some(ReprData { kind: ReprKind::BuiltinInt { builtin, .. }, .. }) => builtin,
226+
_ => Either::Left(BuiltinInt::Isize),
227+
}
228+
}
169229
}
170230

171231
impl HasChildSource<LocalEnumVariantId> for EnumId {

crates/hir-def/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,6 @@ pub enum DefWithBodyId {
479479

480480
impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
481481

482-
// FIXME: Rename EnumVariantId to VariantId so that the macro above can be used
483482
impl From<EnumVariantId> for DefWithBodyId {
484483
fn from(id: EnumVariantId) -> Self {
485484
DefWithBodyId::VariantId(id)

crates/hir-ty/src/consteval.rs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
153153
}
154154
}
155155

156-
fn get_name(variant: EnumVariantId, ctx: &mut ConstEvalCtx<'_>) -> String {
156+
fn get_name(ctx: &mut ConstEvalCtx<'_>, variant: EnumVariantId) -> String {
157157
let loc = variant.parent.lookup(ctx.db.upcast());
158158
let children = variant.parent.child_source(ctx.db.upcast());
159159
let item_tree = loc.id.item_tree(ctx.db.upcast());
@@ -167,20 +167,24 @@ pub fn eval_const(
167167
expr_id: ExprId,
168168
ctx: &mut ConstEvalCtx<'_>,
169169
) -> Result<ComputedExpr, ConstEvalError> {
170+
let u128_to_i128 = |it: u128| -> Result<i128, ConstEvalError> {
171+
it.try_into().map_err(|_| ConstEvalError::NotSupported("u128 is too big"))
172+
};
173+
170174
let expr = &ctx.exprs[expr_id];
171175
match expr {
172176
Expr::Missing => match ctx.owner {
177+
// evaluate the implicit variant index of an enum variant without expression
178+
// FIXME: This should return the type of the enum representation
173179
DefWithBodyId::VariantId(variant) => {
174180
let prev_idx: u32 = variant.local_id.into_raw().into();
175-
let prev_idx = prev_idx.checked_sub(1).map(|idx| Idx::from_raw(RawIdx::from(idx)));
181+
let prev_idx = prev_idx.checked_sub(1).map(RawIdx::from).map(Idx::from_raw);
176182
let value = match prev_idx {
177-
Some(prev) => {
178-
let prev_variant = EnumVariantId { local_id: prev, ..variant };
183+
Some(local_id) => {
184+
let prev_variant = EnumVariantId { local_id, parent: variant.parent };
179185
1 + match ctx.db.const_eval_variant(prev_variant)? {
180186
ComputedExpr::Literal(Literal::Int(v, _)) => v,
181-
ComputedExpr::Literal(Literal::Uint(v, _)) => v
182-
.try_into()
183-
.map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
187+
ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
184188
_ => {
185189
return Err(ConstEvalError::NotSupported(
186190
"Enum can't contain this kind of value",
@@ -206,9 +210,7 @@ pub fn eval_const(
206210
return Ok(ComputedExpr::Literal(Literal::Bool(!b)))
207211
}
208212
ComputedExpr::Literal(Literal::Int(v, _)) => v,
209-
ComputedExpr::Literal(Literal::Uint(v, _)) => v
210-
.try_into()
211-
.map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
213+
ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
212214
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
213215
};
214216
let r = match ty.kind(Interner) {
@@ -237,9 +239,7 @@ pub fn eval_const(
237239
hir_def::expr::UnaryOp::Neg => {
238240
let v = match ev {
239241
ComputedExpr::Literal(Literal::Int(v, _)) => v,
240-
ComputedExpr::Literal(Literal::Uint(v, _)) => v
241-
.try_into()
242-
.map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
242+
ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
243243
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
244244
};
245245
Ok(ComputedExpr::Literal(Literal::Int(
@@ -258,16 +258,12 @@ pub fn eval_const(
258258
let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
259259
let v1 = match lhs {
260260
ComputedExpr::Literal(Literal::Int(v, _)) => v,
261-
ComputedExpr::Literal(Literal::Uint(v, _)) => {
262-
v.try_into().map_err(|_| ConstEvalError::NotSupported("too big u128"))?
263-
}
261+
ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
264262
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
265263
};
266264
let v2 = match rhs {
267265
ComputedExpr::Literal(Literal::Int(v, _)) => v,
268-
ComputedExpr::Literal(Literal::Uint(v, _)) => {
269-
v.try_into().map_err(|_| ConstEvalError::NotSupported("too big u128"))?
270-
}
266+
ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
271267
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
272268
};
273269
match op {
@@ -380,7 +376,7 @@ pub fn eval_const(
380376
}
381377
ValueNs::EnumVariantId(id) => match ctx.db.const_eval_variant(id)? {
382378
ComputedExpr::Literal(lit) => {
383-
Ok(ComputedExpr::Enum(get_name(id, ctx), id, lit))
379+
Ok(ComputedExpr::Enum(get_name(ctx, id), id, lit))
384380
}
385381
_ => Err(ConstEvalError::NotSupported(
386382
"Enums can't evalute to anything but numbers",
@@ -389,6 +385,7 @@ pub fn eval_const(
389385
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
390386
}
391387
}
388+
// FIXME: Handle the cast target
392389
&Expr::Cast { expr, .. } => match eval_const(expr, ctx)? {
393390
ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)),
394391
_ => Err(ConstEvalError::NotSupported("Can't cast these types")),
@@ -463,15 +460,15 @@ pub(crate) fn const_eval_recover(
463460
Err(ConstEvalError::Loop)
464461
}
465462

466-
pub(crate) fn const_eval_recover_variant(
463+
pub(crate) fn const_eval_variant_recover(
467464
_: &dyn HirDatabase,
468465
_: &[String],
469466
_: &EnumVariantId,
470467
) -> Result<ComputedExpr, ConstEvalError> {
471468
Err(ConstEvalError::Loop)
472469
}
473470

474-
pub(crate) fn const_eval_query(
471+
pub(crate) fn const_eval_variant_query(
475472
db: &dyn HirDatabase,
476473
const_id: ConstId,
477474
) -> Result<ComputedExpr, ConstEvalError> {

crates/hir-ty/src/db.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ use arrayvec::ArrayVec;
77
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
88
use hir_def::{
99
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
10-
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, Lookup, TypeOrConstParamId,
11-
VariantId,
10+
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
1211
};
1312
use la_arena::ArenaMap;
1413

@@ -44,12 +43,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
4443
#[salsa::invoke(crate::lower::const_param_ty_query)]
4544
fn const_param_ty(&self, def: ConstParamId) -> Ty;
4645

47-
#[salsa::invoke(crate::consteval::const_eval_query)]
46+
#[salsa::invoke(crate::consteval::const_eval_variant_query)]
4847
#[salsa::cycle(crate::consteval::const_eval_recover)]
4948
fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;
5049

5150
#[salsa::invoke(crate::consteval::const_eval_query_variant)]
52-
#[salsa::cycle(crate::consteval::const_eval_recover_variant)]
51+
#[salsa::cycle(crate::consteval::const_eval_variant_recover)]
5352
fn const_eval_variant(&self, def: EnumVariantId) -> Result<ComputedExpr, ConstEvalError>;
5453

5554
#[salsa::invoke(crate::lower::impl_trait_query)]
@@ -194,11 +193,7 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
194193
db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
195194
}
196195
DefWithBodyId::VariantId(it) => {
197-
let up_db: &dyn DefDatabase = db.upcast();
198-
let loc = it.parent.lookup(up_db);
199-
let item_tree = loc.id.item_tree(up_db);
200-
let konst = &item_tree[loc.id.value];
201-
konst.name.to_string()
196+
db.enum_data(it.parent).variants[it.local_id].name.to_string()
202197
}
203198
});
204199
db.infer_query(def)

crates/hir-ty/src/infer.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use std::sync::Arc;
1919
use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
2020
use hir_def::{
2121
body::Body,
22+
builtin_type::BuiltinType,
2223
data::{ConstData, StaticData},
2324
expr::{BindingAnnotation, ExprId, PatId},
2425
lang_item::LangItemTarget,
@@ -68,8 +69,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
6869
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
6970
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
7071
DefWithBodyId::VariantId(v) => {
71-
// FIXME: This should return the `repr(...)` type of the enum
72-
ctx.return_ty = TyBuilder::def_ty(db, v.parent.into()).fill_with_unknown().build()
72+
ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
73+
Either::Left(builtin) => BuiltinType::Int(builtin),
74+
Either::Right(builtin) => BuiltinType::Uint(builtin),
75+
});
7376
}
7477
}
7578

crates/hir-ty/src/tests.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,18 @@ fn visit_module(
461461
let body = db.body(def);
462462
visit_body(db, &body, cb);
463463
}
464+
ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => {
465+
db.enum_data(it)
466+
.variants
467+
.iter()
468+
.map(|(id, _)| hir_def::EnumVariantId { parent: it, local_id: id })
469+
.for_each(|it| {
470+
let def = it.into();
471+
cb(def);
472+
let body = db.body(def);
473+
visit_body(db, &body, cb);
474+
});
475+
}
464476
ModuleDefId::TraitId(it) => {
465477
let trait_data = db.trait_data(it);
466478
for &(_, item) in trait_data.items.iter() {

0 commit comments

Comments
 (0)