Skip to content

Commit a0be16b

Browse files
committed
Auto merge of rust-lang#14040 - HKalbasi:mir, r=HKalbasi
Beginning of MIR This pull request introduces the initial implementation of MIR lowering and interpreting in Rust Analyzer. The implementation of MIR has potential to bring several benefits: - Executing a unit test without compiling it: This is my main goal. It can be useful for quickly testing code changes and print-debugging unit tests without the need for a full compilation (ideally in almost zero time, similar to languages like python and js). There is a probability that it goes nowhere, it might become slower than rustc, or it might need some unreasonable amount of memory, or we may fail to support a common pattern/function that make it unusable for most of the codes. - Constant evaluation: MIR allows for easier and more correct constant evaluation, on par with rustc. If r-a wants to fully support the type system, it needs full const eval, which means arbitrary code execution, which needs MIR or something similar. - Supporting more diagnostics: MIR can be used to detect errors, most famously borrow checker and lifetime errors, but also mutability errors and uninitialized variables, which can be difficult/impossible to detect in HIR. - Lowering closures: With MIR we can find out closure capture modes, which is useful in detecting if a closure implements the `FnMut` or `Fn` traits, and calculating its size and data layout. But the current PR implements no diagnostics and doesn't support closures. About const eval, I removed the old const eval code and it now uses the mir interpreter. Everything that is supported in stable rustc is either implemented or is super easy to implement. About interpreting unit tests, I added an experimental config, disabled by default, that shows a `pass` or `fail` on hover of unit tests (ideally it should be a button similar to `Run test` button, but I didn't figured out how to add them). Currently, no real world test works, due to missing features including closures, heap allocation, `dyn Trait` and ... so at this point it is only useful for me selecting what to implement next. The implementation of MIR is based on the design of rustc, the data structures are almost copy paste (so it should be easy to migrate it to a possible future stable-mir), but the lowering and interpreting code is from me.
2 parents c867cbf + cd67589 commit a0be16b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+4447
-697
lines changed

crates/hir-def/src/expr.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,21 @@ pub type LabelId = Idx<Label>;
5252
// We convert float values into bits and that's how we don't need to deal with f32 and f64.
5353
// For PartialEq, bits comparison should work, as ordering is not important
5454
// https://github.com/rust-lang/rust-analyzer/issues/12380#issuecomment-1137284360
55-
#[derive(Default, Debug, Clone, Eq, PartialEq)]
55+
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
5656
pub struct FloatTypeWrapper(u64);
5757

5858
impl FloatTypeWrapper {
5959
pub fn new(value: f64) -> Self {
6060
Self(value.to_bits())
6161
}
62+
63+
pub fn into_f64(self) -> f64 {
64+
f64::from_bits(self.0)
65+
}
66+
67+
pub fn into_f32(self) -> f32 {
68+
f64::from_bits(self.0) as f32
69+
}
6270
}
6371

6472
impl fmt::Display for FloatTypeWrapper {

crates/hir-def/src/lang_item.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,15 @@ impl LangItems {
181181
T: Into<AttrDefId> + Copy,
182182
{
183183
let _p = profile::span("collect_lang_item");
184-
if let Some(lang_item) = lang_attr(db, item).and_then(|it| LangItem::from_str(&it)) {
184+
if let Some(lang_item) = lang_attr(db, item) {
185185
self.items.entry(lang_item).or_insert_with(|| constructor(item));
186186
}
187187
}
188188
}
189189

190-
pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<SmolStr> {
190+
pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<LangItem> {
191191
let attrs = db.attrs(item.into());
192-
attrs.by_key("lang").string_value().cloned()
192+
attrs.by_key("lang").string_value().cloned().and_then(|it| LangItem::from_str(&it))
193193
}
194194

195195
pub enum GenericRequirement {

crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ macro_rules! assert {
143143
144144
fn main() {
145145
{
146-
if !true {
146+
if !(true ) {
147147
$crate::panic!("{} {:?}", arg1(a, b, c), arg2);
148148
}
149149
};

crates/hir-def/src/path.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88

99
use crate::{
1010
body::LowerCtx,
11-
type_ref::{ConstScalarOrPath, LifetimeRef},
11+
type_ref::{ConstRefOrPath, LifetimeRef},
1212
};
1313
use hir_expand::name::Name;
1414
use intern::Interned;
@@ -85,7 +85,7 @@ pub struct AssociatedTypeBinding {
8585
pub enum GenericArg {
8686
Type(TypeRef),
8787
Lifetime(LifetimeRef),
88-
Const(ConstScalarOrPath),
88+
Const(ConstRefOrPath),
8989
}
9090

9191
impl Path {

crates/hir-def/src/path/lower.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::iter;
44

5-
use crate::type_ref::ConstScalarOrPath;
5+
use crate::type_ref::ConstRefOrPath;
66

77
use either::Either;
88
use hir_expand::name::{name, AsName};
@@ -212,7 +212,7 @@ pub(super) fn lower_generic_args(
212212
}
213213
}
214214
ast::GenericArg::ConstArg(arg) => {
215-
let arg = ConstScalarOrPath::from_expr_opt(arg.expr());
215+
let arg = ConstRefOrPath::from_expr_opt(arg.expr());
216216
args.push(GenericArg::Const(arg))
217217
}
218218
}

crates/hir-def/src/type_ref.rs

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ pub enum TypeRef {
116116
Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
117117
// FIXME: for full const generics, the latter element (length) here is going to have to be an
118118
// expression that is further lowered later in hir_ty.
119-
Array(Box<TypeRef>, ConstScalarOrPath),
119+
Array(Box<TypeRef>, ConstRefOrPath),
120120
Slice(Box<TypeRef>),
121121
/// A fn pointer. Last element of the vector is the return type.
122122
Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
@@ -188,7 +188,7 @@ impl TypeRef {
188188
// `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
189189
// `hir_ty` level, which would allow knowing the type of:
190190
// let v: [u8; 2 + 2] = [0u8; 4];
191-
let len = ConstScalarOrPath::from_expr_opt(inner.expr());
191+
let len = ConstRefOrPath::from_expr_opt(inner.expr());
192192
TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
193193
}
194194
ast::Type::SliceType(inner) => {
@@ -378,25 +378,25 @@ impl TypeBound {
378378
}
379379

380380
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
381-
pub enum ConstScalarOrPath {
382-
Scalar(ConstScalar),
381+
pub enum ConstRefOrPath {
382+
Scalar(ConstRef),
383383
Path(Name),
384384
}
385385

386-
impl std::fmt::Display for ConstScalarOrPath {
386+
impl std::fmt::Display for ConstRefOrPath {
387387
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
388388
match self {
389-
ConstScalarOrPath::Scalar(s) => s.fmt(f),
390-
ConstScalarOrPath::Path(n) => n.fmt(f),
389+
ConstRefOrPath::Scalar(s) => s.fmt(f),
390+
ConstRefOrPath::Path(n) => n.fmt(f),
391391
}
392392
}
393393
}
394394

395-
impl ConstScalarOrPath {
395+
impl ConstRefOrPath {
396396
pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
397397
match expr {
398398
Some(x) => Self::from_expr(x),
399-
None => Self::Scalar(ConstScalar::Unknown),
399+
None => Self::Scalar(ConstRef::Unknown),
400400
}
401401
}
402402

@@ -407,16 +407,16 @@ impl ConstScalarOrPath {
407407
ast::Expr::PathExpr(p) => {
408408
match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
409409
Some(x) => Self::Path(x.as_name()),
410-
None => Self::Scalar(ConstScalar::Unknown),
410+
None => Self::Scalar(ConstRef::Unknown),
411411
}
412412
}
413413
ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
414414
Some(ast::UnaryOp::Neg) => {
415415
let unsigned = Self::from_expr_opt(prefix_expr.expr());
416416
// Add sign
417417
match unsigned {
418-
Self::Scalar(ConstScalar::UInt(num)) => {
419-
Self::Scalar(ConstScalar::Int(-(num as i128)))
418+
Self::Scalar(ConstRef::UInt(num)) => {
419+
Self::Scalar(ConstRef::Int(-(num as i128)))
420420
}
421421
other => other,
422422
}
@@ -425,22 +425,22 @@ impl ConstScalarOrPath {
425425
},
426426
ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
427427
ast::LiteralKind::IntNumber(num) => {
428-
num.value().map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown)
428+
num.value().map(ConstRef::UInt).unwrap_or(ConstRef::Unknown)
429429
}
430430
ast::LiteralKind::Char(c) => {
431-
c.value().map(ConstScalar::Char).unwrap_or(ConstScalar::Unknown)
431+
c.value().map(ConstRef::Char).unwrap_or(ConstRef::Unknown)
432432
}
433-
ast::LiteralKind::Bool(f) => ConstScalar::Bool(f),
434-
_ => ConstScalar::Unknown,
433+
ast::LiteralKind::Bool(f) => ConstRef::Bool(f),
434+
_ => ConstRef::Unknown,
435435
}),
436-
_ => Self::Scalar(ConstScalar::Unknown),
436+
_ => Self::Scalar(ConstRef::Unknown),
437437
}
438438
}
439439
}
440440

441441
/// A concrete constant value
442-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
443-
pub enum ConstScalar {
442+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
443+
pub enum ConstRef {
444444
Int(i128),
445445
UInt(u128),
446446
Bool(bool),
@@ -454,18 +454,18 @@ pub enum ConstScalar {
454454
Unknown,
455455
}
456456

457-
impl ConstScalar {
457+
impl ConstRef {
458458
pub fn builtin_type(&self) -> BuiltinType {
459459
match self {
460-
ConstScalar::UInt(_) | ConstScalar::Unknown => BuiltinType::Uint(BuiltinUint::U128),
461-
ConstScalar::Int(_) => BuiltinType::Int(BuiltinInt::I128),
462-
ConstScalar::Char(_) => BuiltinType::Char,
463-
ConstScalar::Bool(_) => BuiltinType::Bool,
460+
ConstRef::UInt(_) | ConstRef::Unknown => BuiltinType::Uint(BuiltinUint::U128),
461+
ConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
462+
ConstRef::Char(_) => BuiltinType::Char,
463+
ConstRef::Bool(_) => BuiltinType::Bool,
464464
}
465465
}
466466
}
467467

468-
impl From<Literal> for ConstScalar {
468+
impl From<Literal> for ConstRef {
469469
fn from(literal: Literal) -> Self {
470470
match literal {
471471
Literal::Char(c) => Self::Char(c),
@@ -477,14 +477,14 @@ impl From<Literal> for ConstScalar {
477477
}
478478
}
479479

480-
impl std::fmt::Display for ConstScalar {
480+
impl std::fmt::Display for ConstRef {
481481
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
482482
match self {
483-
ConstScalar::Int(num) => num.fmt(f),
484-
ConstScalar::UInt(num) => num.fmt(f),
485-
ConstScalar::Bool(flag) => flag.fmt(f),
486-
ConstScalar::Char(c) => write!(f, "'{c}'"),
487-
ConstScalar::Unknown => f.write_char('_'),
483+
ConstRef::Int(num) => num.fmt(f),
484+
ConstRef::UInt(num) => num.fmt(f),
485+
ConstRef::Bool(flag) => flag.fmt(f),
486+
ConstRef::Char(c) => write!(f, "'{c}'"),
487+
ConstRef::Unknown => f.write_char('_'),
488488
}
489489
}
490490
}

crates/hir-expand/src/builtin_fn_macro.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ fn assert_expand(
206206
let cond = cond.clone();
207207
let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
208208
quote! {{
209-
if !#cond {
209+
if !(#cond) {
210210
#DOLLAR_CRATE::panic!(##panic_args);
211211
}
212212
}}

crates/hir-ty/src/builder.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ impl TyBuilder<()> {
152152
TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner)
153153
}
154154

155+
// FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well
156+
pub fn discr_ty() -> Ty {
157+
TyKind::Scalar(chalk_ir::Scalar::Int(chalk_ir::IntTy::I128)).intern(Interner)
158+
}
159+
160+
pub fn bool() -> Ty {
161+
TyKind::Scalar(chalk_ir::Scalar::Bool).intern(Interner)
162+
}
163+
155164
pub fn usize() -> Ty {
156165
TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(Interner)
157166
}

crates/hir-ty/src/chalk_db.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,8 +540,7 @@ pub(crate) fn trait_datum_query(
540540
let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
541541
let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect();
542542
let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
543-
let well_known = lang_attr(db.upcast(), trait_)
544-
.and_then(|name| well_known_trait_from_lang_item(LangItem::from_str(&name)?));
543+
let well_known = lang_attr(db.upcast(), trait_).and_then(well_known_trait_from_lang_item);
545544
let trait_datum = TraitDatum {
546545
id: trait_id,
547546
binders: make_binders(db, &generic_params, trait_datum_bound),

0 commit comments

Comments
 (0)