From 4d035317041e54111ac0739d7bbc75a8eb71fece Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Sat, 18 Apr 2020 22:18:42 +0200 Subject: [PATCH] misc: literal out of range errors --- crates/mun_codegen/src/ir/body.rs | 6 +- crates/mun_codegen/src/ir/ty.rs | 58 ++++------------- crates/mun_hir/src/code_model.rs | 4 +- crates/mun_hir/src/diagnostics.rs | 5 +- crates/mun_hir/src/expr.rs | 2 +- crates/mun_hir/src/expr/validator.rs | 25 ++++---- .../expr/validator/literal_out_of_range.rs | 43 +++++++++++++ crates/mun_hir/src/expr/validator/tests.rs | 2 +- .../expr/validator/uninitialized_access.rs | 62 ++++++++++++------- crates/mun_hir/src/lib.rs | 5 +- crates/mun_hir/src/mock.rs | 3 + crates/mun_hir/src/ty.rs | 2 + crates/mun_hir/src/ty/infer.rs | 29 +++++++-- crates/mun_hir/src/ty/primitives.rs | 37 +++++++++++ crates/mun_hir/src/ty/resolve.rs | 49 +++++++++++++++ .../tests__infer_suffix_literals.snap | 12 +++- crates/mun_hir/src/ty/tests.rs | 4 ++ 17 files changed, 254 insertions(+), 94 deletions(-) create mode 100644 crates/mun_hir/src/expr/validator/literal_out_of_range.rs create mode 100644 crates/mun_hir/src/ty/resolve.rs diff --git a/crates/mun_codegen/src/ir/body.rs b/crates/mun_codegen/src/ir/body.rs index 7f4431341..e0763edd6 100644 --- a/crates/mun_codegen/src/ir/body.rs +++ b/crates/mun_codegen/src/ir/body.rs @@ -14,7 +14,7 @@ use inkwell::{ }; use std::{collections::HashMap, sync::Arc}; -use crate::ir::ty::ResolveBitness; +use hir::ResolveBitness; use inkwell::basic_block::BasicBlock; use inkwell::values::{AggregateValueEnum, GlobalValue, PointerValue}; @@ -275,7 +275,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { let context = self.db.context(); let has_sign = ty.signedness == hir::Signedness::Signed; - let ir_ty = match ty.resolve(&self.db.target_data()).bitness { + let ir_ty = match ty.resolve(&self.db.target_data_layout()).bitness { hir::IntBitness::X8 => context.i8_type().const_int(v.value as u64, has_sign), hir::IntBitness::X16 => context.i16_type().const_int(v.value as u64, has_sign), hir::IntBitness::X32 => context.i32_type().const_int(v.value as u64, has_sign), @@ -299,7 +299,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { }; let context = self.db.context(); - let ir_ty = match ty.resolve(&self.db.target_data()).bitness { + let ir_ty = match ty.bitness.resolve(&self.db.target_data_layout()) { hir::FloatBitness::X32 => context.f32_type().const_float(v.value), hir::FloatBitness::X64 => context.f64_type().const_float(v.value), _ => unreachable!("unresolved bitness in code generation"), diff --git a/crates/mun_codegen/src/ir/ty.rs b/crates/mun_codegen/src/ir/ty.rs index 318eaf7a1..a5df06da6 100644 --- a/crates/mun_codegen/src/ir/ty.rs +++ b/crates/mun_codegen/src/ir/ty.rs @@ -1,13 +1,17 @@ use super::try_convert_any_to_basic; -use crate::type_info::TypeSize; use crate::{ + type_info::TypeSize, type_info::{TypeGroup, TypeInfo}, CodeGenParams, IrDatabase, }; -use hir::{ApplicationTy, CallableDef, FloatBitness, FloatTy, IntBitness, IntTy, Ty, TypeCtor}; -use inkwell::targets::TargetData; -use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum, FloatType, IntType, StructType}; -use inkwell::AddressSpace; +use hir::{ + ApplicationTy, CallableDef, FloatBitness, FloatTy, IntBitness, IntTy, ResolveBitness, Ty, + TypeCtor, +}; +use inkwell::{ + types::{AnyTypeEnum, BasicType, BasicTypeEnum, FloatType, IntType, StructType}, + AddressSpace, +}; /// Given a mun type, construct an LLVM IR type #[rustfmt::skip] @@ -57,7 +61,7 @@ pub(crate) fn ir_query(db: &impl IrDatabase, ty: Ty, params: CodeGenParams) -> A /// Returns the LLVM IR type of the specified float type fn float_ty_query(db: &impl IrDatabase, fty: FloatTy) -> FloatType { let context = db.context(); - match fty.resolve(&db.target_data()).bitness { + match fty.bitness.resolve(&db.target_data_layout()) { FloatBitness::X64 => context.f64_type(), FloatBitness::X32 => context.f32_type(), _ => unreachable!(), @@ -67,7 +71,7 @@ fn float_ty_query(db: &impl IrDatabase, fty: FloatTy) -> FloatType { /// Returns the LLVM IR type of the specified int type fn int_ty_query(db: &impl IrDatabase, ity: IntTy) -> IntType { let context = db.context(); - match ity.resolve(&db.target_data()).bitness { + match ity.bitness.resolve(&db.target_data_layout()) { IntBitness::X128 => context.i128_type(), IntBitness::X64 => context.i64_type(), IntBitness::X32 => context.i32_type(), @@ -102,7 +106,7 @@ pub fn type_info_query(db: &impl IrDatabase, ty: Ty) -> TypeInfo { let ir_ty = float_ty_query(db, ty); let type_size = TypeSize::from_ir_type(&ir_ty, target.as_ref()); TypeInfo::new( - format!("core::{}", ty.resolve(&db.target_data())), + format!("core::{}", ty.resolve(&db.target_data_layout())), TypeGroup::FundamentalTypes, type_size, ) @@ -111,7 +115,7 @@ pub fn type_info_query(db: &impl IrDatabase, ty: Ty) -> TypeInfo { let ir_ty = int_ty_query(db, ty); let type_size = TypeSize::from_ir_type(&ir_ty, target.as_ref()); TypeInfo::new( - format!("core::{}", ty.resolve(&db.target_data())), + format!("core::{}", ty.resolve(&db.target_data_layout())), TypeGroup::FundamentalTypes, type_size, ) @@ -131,39 +135,3 @@ pub fn type_info_query(db: &impl IrDatabase, ty: Ty) -> TypeInfo { _ => unreachable!("{:?} unhandled", ty), } } - -pub(crate) trait ResolveBitness { - fn resolve(&self, target: &TargetData) -> Self; -} - -impl ResolveBitness for FloatTy { - fn resolve(&self, _target: &TargetData) -> Self { - let bitness = match self.bitness { - FloatBitness::Undefined => FloatBitness::X64, - bitness => bitness, - }; - FloatTy { bitness } - } -} - -impl ResolveBitness for IntTy { - fn resolve(&self, target: &TargetData) -> Self { - let ptr_bit_size = target.ptr_sized_int_type(None).get_bit_width(); - let bitness = match ptr_bit_size { - 16 => IntBitness::X16, - 32 => IntBitness::X32, - 64 => IntBitness::X64, - 128 => IntBitness::X128, - _ => unreachable!("unsupported bit size for pointers"), - }; - let bitness = match self.bitness { - IntBitness::Undefined => IntBitness::X64, - IntBitness::Xsize => bitness, - bitness => bitness, - }; - IntTy { - bitness, - signedness: self.signedness, - } - } -} diff --git a/crates/mun_hir/src/code_model.rs b/crates/mun_hir/src/code_model.rs index da23c69f0..f7a083af1 100644 --- a/crates/mun_hir/src/code_model.rs +++ b/crates/mun_hir/src/code_model.rs @@ -353,8 +353,8 @@ impl Function { body.add_diagnostics(db, self.into(), sink); let infer = self.infer(db); infer.add_diagnostics(db, self, sink); - let mut validator = ExprValidator::new(self, db, sink); - validator.validate_body(); + let validator = ExprValidator::new(self, db); + validator.validate_body(sink); } } diff --git a/crates/mun_hir/src/diagnostics.rs b/crates/mun_hir/src/diagnostics.rs index 9015ab36a..72fcc9704 100644 --- a/crates/mun_hir/src/diagnostics.rs +++ b/crates/mun_hir/src/diagnostics.rs @@ -1,6 +1,6 @@ use crate::adt::StructKind; use crate::in_file::InFile; -use crate::{FileId, HirDatabase, Name, Ty}; +use crate::{FileId, HirDatabase, IntTy, Name, Ty}; use mun_syntax::{ast, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange}; use std::{any::Any, fmt}; @@ -581,11 +581,12 @@ impl Diagnostic for IntLiteralTooLarge { #[derive(Debug)] pub struct LiteralOutOfRange { pub literal: InFile>, + pub int_ty: IntTy, } impl Diagnostic for LiteralOutOfRange { fn message(&self) -> String { - "literal out of range".to_owned() + format!("literal out of range for `{}`", self.int_ty.ty_to_string()) } fn source(&self) -> InFile { diff --git a/crates/mun_hir/src/expr.rs b/crates/mun_hir/src/expr.rs index 48bcfe529..07d3e89ef 100644 --- a/crates/mun_hir/src/expr.rs +++ b/crates/mun_hir/src/expr.rs @@ -1003,7 +1003,7 @@ fn integer_lit(str: &str, suffix: Option<&str>) -> (Literal, Vec) if from_lexer { (0, Some(LiteralError::LexerError)) } else { - (u128::max_value(), Some(LiteralError::IntTooLarge)) + (0, Some(LiteralError::IntTooLarge)) } } }; diff --git a/crates/mun_hir/src/expr/validator.rs b/crates/mun_hir/src/expr/validator.rs index 0ab43901d..1e9452cee 100644 --- a/crates/mun_hir/src/expr/validator.rs +++ b/crates/mun_hir/src/expr/validator.rs @@ -6,26 +6,25 @@ use crate::{diagnostics::DiagnosticSink, Body, Expr, Function, HirDatabase, Infe use mun_syntax::{AstNode, SyntaxNodePtr}; use std::sync::Arc; +mod literal_out_of_range; mod uninitialized_access; #[cfg(test)] mod tests; -pub struct ExprValidator<'a, 'b: 'a, 'd, DB: HirDatabase> { +pub struct ExprValidator<'d, DB: HirDatabase> { func: Function, infer: Arc, body: Arc, body_source_map: Arc, - sink: &'a mut DiagnosticSink<'b>, db: &'d DB, } -impl<'a, 'b, 'd, DB: HirDatabase> ExprValidator<'a, 'b, 'd, DB> { - pub fn new(func: Function, db: &'d DB, sink: &'a mut DiagnosticSink<'b>) -> Self { +impl<'d, DB: HirDatabase> ExprValidator<'d, DB> { + pub fn new(func: Function, db: &'d DB) -> Self { let (body, body_source_map) = db.body_with_source_map(func.into()); ExprValidator { func, - sink, db, infer: db.infer(func.into()), body, @@ -33,11 +32,13 @@ impl<'a, 'b, 'd, DB: HirDatabase> ExprValidator<'a, 'b, 'd, DB> { } } - pub fn validate_body(&mut self) { - self.validate_uninitialized_access(); - self.validate_extern(); + pub fn validate_body(&self, sink: &mut DiagnosticSink) { + self.validate_literal_ranges(sink); + self.validate_uninitialized_access(sink); + self.validate_extern(sink); } - pub fn validate_extern(&mut self) { + + pub fn validate_extern(&self, sink: &mut DiagnosticSink) { if !self.func.is_extern(self.db) { return; } @@ -45,7 +46,7 @@ impl<'a, 'b, 'd, DB: HirDatabase> ExprValidator<'a, 'b, 'd, DB> { // Validate that there is no body match self.body[self.func.body(self.db).body_expr] { Expr::Missing => {} - _ => self.sink.push(ExternCannotHaveBody { + _ => sink.push(ExternCannotHaveBody { func: self .func .source(self.db) @@ -62,7 +63,7 @@ impl<'a, 'b, 'd, DB: HirDatabase> ExprValidator<'a, 'b, 'd, DB> { .type_ref_syntax(*ty_ref) .map(|ptr| ptr.syntax_node_ptr()) .unwrap(); - self.sink.push(ExternNonPrimitiveParam { + sink.push(ExternNonPrimitiveParam { param: InFile::new(self.func.source(self.db).file_id, arg_ptr), }) } @@ -75,7 +76,7 @@ impl<'a, 'b, 'd, DB: HirDatabase> ExprValidator<'a, 'b, 'd, DB> { .type_ref_syntax(*fn_data.ret_type()) .map(|ptr| ptr.syntax_node_ptr()) .unwrap(); - self.sink.push(ExternNonPrimitiveParam { + sink.push(ExternNonPrimitiveParam { param: InFile::new(self.func.source(self.db).file_id, arg_ptr), }) } diff --git a/crates/mun_hir/src/expr/validator/literal_out_of_range.rs b/crates/mun_hir/src/expr/validator/literal_out_of_range.rs new file mode 100644 index 000000000..c926159b9 --- /dev/null +++ b/crates/mun_hir/src/expr/validator/literal_out_of_range.rs @@ -0,0 +1,43 @@ +use super::ExprValidator; +use crate::diagnostics::{DiagnosticSink, LiteralOutOfRange}; +use crate::ty::ResolveBitness; +use crate::{ty_app, TypeCtor}; +use crate::{Expr, HirDatabase, HirDisplay, Literal}; + +impl<'d, D: HirDatabase> ExprValidator<'d, D> { + /// Iterates over all expressions to determine if one of the literals has a value that is out of + /// range of its type. + pub fn validate_literal_ranges(&self, sink: &mut DiagnosticSink) { + self.body[self.body.body_expr].walk_child_exprs(move |expr_id| { + let expr = &self.body[expr_id]; + if let Expr::Literal(Literal::Int(lit)) = &expr { + let ty = &self.infer[expr_id]; + match ty { + ty_app!(TypeCtor::Int(int_ty)) => { + if lit.value > int_ty.resolve(&self.db.target_data_layout()).max() { + let literal = self + .body_source_map + .expr_syntax(expr_id) + .expect("could not retrieve expr from source map") + .map(|expr_src| { + expr_src + .left() + .expect("could not retrieve expr from ExprSource") + .cast() + .expect("could not cast expression to literal") + }); + sink.push(LiteralOutOfRange { + literal, + int_ty: *int_ty, + }) + } + } + _ => panic!( + "expected int literal to have int ty while instead it is `{}`", + ty.display(self.db) + ), + } + } + }) + } +} diff --git a/crates/mun_hir/src/expr/validator/tests.rs b/crates/mun_hir/src/expr/validator/tests.rs index 7a2cccaa7..da0cceee6 100644 --- a/crates/mun_hir/src/expr/validator/tests.rs +++ b/crates/mun_hir/src/expr/validator/tests.rs @@ -82,7 +82,7 @@ fn diagnostics(content: &str) -> String { let fun = Function { id: ctx.to_def(&def), }; - ExprValidator::new(fun, &db, &mut diag_sink).validate_body(); + ExprValidator::new(fun, &db).validate_body(&mut diag_sink); } } drop(diag_sink); diff --git a/crates/mun_hir/src/expr/validator/uninitialized_access.rs b/crates/mun_hir/src/expr/validator/uninitialized_access.rs index 3c57c4605..274ef2bdc 100644 --- a/crates/mun_hir/src/expr/validator/uninitialized_access.rs +++ b/crates/mun_hir/src/expr/validator/uninitialized_access.rs @@ -1,5 +1,5 @@ use super::ExprValidator; -use crate::diagnostics::PossiblyUninitializedVariable; +use crate::diagnostics::{DiagnosticSink, PossiblyUninitializedVariable}; use crate::{BinaryOp, Expr, ExprId, HirDatabase, PatId, Path, Resolution, Resolver, Statement}; use std::collections::HashSet; @@ -10,9 +10,9 @@ enum ExprKind { Both, } -impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { +impl<'d, D: HirDatabase> ExprValidator<'d, D> { /// Validates that all binding access has previously been initialized. - pub(super) fn validate_uninitialized_access(&mut self) { + pub(super) fn validate_uninitialized_access(&self, sink: &mut DiagnosticSink) { let mut initialized_patterns = HashSet::new(); // Add all parameter patterns to the set of initialized patterns (they must have been @@ -22,6 +22,7 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { } self.validate_expr_access( + sink, &mut initialized_patterns, self.body.body_expr, ExprKind::Normal, @@ -30,7 +31,8 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { /// Validates that the specified expr does not access unitialized bindings fn validate_expr_access( - &mut self, + &self, + sink: &mut DiagnosticSink, initialized_patterns: &mut HashSet, expr: ExprId, expr_side: ExprKind, @@ -38,23 +40,31 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { let body = self.body.clone(); match &body[expr] { Expr::Call { callee, args } => { - self.validate_expr_access(initialized_patterns, *callee, expr_side); + self.validate_expr_access(sink, initialized_patterns, *callee, expr_side); for arg in args.iter() { - self.validate_expr_access(initialized_patterns, *arg, expr_side); + self.validate_expr_access(sink, initialized_patterns, *arg, expr_side); } } Expr::Path(p) => { let resolver = crate::expr::resolver_for_expr(self.body.clone(), self.db, expr); - self.validate_path_access(initialized_patterns, &resolver, p, expr, expr_side); + self.validate_path_access( + sink, + initialized_patterns, + &resolver, + p, + expr, + expr_side, + ); } Expr::If { condition, then_branch, else_branch, } => { - self.validate_expr_access(initialized_patterns, *condition, ExprKind::Normal); + self.validate_expr_access(sink, initialized_patterns, *condition, ExprKind::Normal); let mut then_branch_initialized_patterns = initialized_patterns.clone(); self.validate_expr_access( + sink, &mut then_branch_initialized_patterns, *then_branch, ExprKind::Normal, @@ -62,6 +72,7 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { if let Some(else_branch) = else_branch { let mut else_branch_initialized_patterns = initialized_patterns.clone(); self.validate_expr_access( + sink, &mut else_branch_initialized_patterns, *else_branch, ExprKind::Normal, @@ -86,7 +97,7 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { } } Expr::UnaryOp { expr, .. } => { - self.validate_expr_access(initialized_patterns, *expr, ExprKind::Normal); + self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal); } Expr::BinaryOp { lhs, rhs, op } => { let lhs_expr_kind = match op { @@ -94,8 +105,8 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { Some(BinaryOp::Assignment { op: None }) => ExprKind::Place, _ => ExprKind::Normal, }; - self.validate_expr_access(initialized_patterns, *lhs, lhs_expr_kind); - self.validate_expr_access(initialized_patterns, *rhs, ExprKind::Normal) + self.validate_expr_access(sink, initialized_patterns, *lhs, lhs_expr_kind); + self.validate_expr_access(sink, initialized_patterns, *rhs, ExprKind::Normal) } Expr::Block { statements, tail } => { for statement in statements.iter() { @@ -105,6 +116,7 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { } => { if let Some(initializer) = initializer { self.validate_expr_access( + sink, initialized_patterns, *initializer, ExprKind::Normal, @@ -114,6 +126,7 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { } Statement::Expr(expr) => { self.validate_expr_access( + sink, initialized_patterns, *expr, ExprKind::Normal, @@ -125,25 +138,26 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { } } if let Some(tail) = tail { - self.validate_expr_access(initialized_patterns, *tail, ExprKind::Normal) + self.validate_expr_access(sink, initialized_patterns, *tail, ExprKind::Normal) } } Expr::Return { expr } => { if let Some(expr) = expr { - self.validate_expr_access(initialized_patterns, *expr, ExprKind::Normal) + self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal) } } Expr::Break { expr } => { if let Some(expr) = expr { - self.validate_expr_access(initialized_patterns, *expr, ExprKind::Normal) + self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal) } } Expr::Loop { body } => { - self.validate_expr_access(initialized_patterns, *body, ExprKind::Normal) + self.validate_expr_access(sink, initialized_patterns, *body, ExprKind::Normal) } Expr::While { condition, body } => { - self.validate_expr_access(initialized_patterns, *condition, ExprKind::Normal); + self.validate_expr_access(sink, initialized_patterns, *condition, ExprKind::Normal); self.validate_expr_access( + sink, &mut initialized_patterns.clone(), *body, ExprKind::Normal, @@ -151,14 +165,19 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { } Expr::RecordLit { fields, spread, .. } => { for field in fields.iter() { - self.validate_expr_access(initialized_patterns, field.expr, ExprKind::Normal); + self.validate_expr_access( + sink, + initialized_patterns, + field.expr, + ExprKind::Normal, + ); } if let Some(expr) = spread { - self.validate_expr_access(initialized_patterns, *expr, ExprKind::Normal); + self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal); } } Expr::Field { expr, .. } => { - self.validate_expr_access(initialized_patterns, *expr, ExprKind::Normal); + self.validate_expr_access(sink, initialized_patterns, *expr, ExprKind::Normal); } Expr::Literal(_) => {} Expr::Missing => {} @@ -166,7 +185,8 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { } fn validate_path_access( - &mut self, + &self, + sink: &mut DiagnosticSink, initialized_patterns: &mut HashSet, resolver: &Resolver, path: &Path, @@ -193,7 +213,7 @@ impl<'a, 'b, 'd, D: HirDatabase> ExprValidator<'a, 'b, 'd, D> { // Check if the binding has already been initialized if initialized_patterns.get(&pat).is_none() { let (_, body_source_map) = self.db.body_with_source_map(self.func.into()); - self.sink.push(PossiblyUninitializedVariable { + sink.push(PossiblyUninitializedVariable { file: self.func.module(self.db).file_id(), pat: body_source_map .expr_syntax(expr) diff --git a/crates/mun_hir/src/lib.rs b/crates/mun_hir/src/lib.rs index 001ef2826..2ed324d7e 100644 --- a/crates/mun_hir/src/lib.rs +++ b/crates/mun_hir/src/lib.rs @@ -58,7 +58,10 @@ pub use crate::{ path::{Path, PathKind}, raw::RawItems, resolve::{Resolution, Resolver}, - ty::{lower::CallableDef, ApplicationTy, FloatTy, InferenceResult, IntTy, Ty, TypeCtor}, + ty::{ + lower::CallableDef, ApplicationTy, FloatTy, InferenceResult, IntTy, ResolveBitness, Ty, + TypeCtor, + }, }; use crate::{ diff --git a/crates/mun_hir/src/mock.rs b/crates/mun_hir/src/mock.rs index 4af824cbd..b151be167 100644 --- a/crates/mun_hir/src/mock.rs +++ b/crates/mun_hir/src/mock.rs @@ -1,6 +1,8 @@ +use crate::db::HirDatabase; use crate::db::SourceDatabase; use crate::input::{SourceRoot, SourceRootId}; use crate::{FileId, RelativePathBuf}; +use mun_target::spec::Target; use parking_lot::Mutex; use std::sync::Arc; @@ -40,6 +42,7 @@ impl MockDatabase { let text = Arc::new(text.to_owned()); let rel_path = RelativePathBuf::from("main.mun"); let file_id = FileId(0); + db.set_target(Target::host_target().unwrap()); db.set_file_relative_path(file_id, rel_path.clone()); db.set_file_text(file_id, Arc::new(text.to_string())); db.set_file_source_root(file_id, source_root_id); diff --git a/crates/mun_hir/src/ty.rs b/crates/mun_hir/src/ty.rs index 31897b006..25ea5562b 100644 --- a/crates/mun_hir/src/ty.rs +++ b/crates/mun_hir/src/ty.rs @@ -2,6 +2,7 @@ mod infer; pub(super) mod lower; mod op; mod primitives; +mod resolve; use crate::display::{HirDisplay, HirFormatter}; use crate::ty::infer::TypeVarId; @@ -11,6 +12,7 @@ pub(crate) use infer::infer_query; pub use infer::InferenceResult; pub(crate) use lower::{callable_item_sig, fn_sig_for_fn, type_for_def, CallableDef, TypableDef}; pub use primitives::{FloatTy, IntTy}; +pub use resolve::ResolveBitness; use std::fmt; use std::sync::Arc; diff --git a/crates/mun_hir/src/ty/infer.rs b/crates/mun_hir/src/ty/infer.rs index 4350af947..4aa4e6561 100644 --- a/crates/mun_hir/src/ty/infer.rs +++ b/crates/mun_hir/src/ty/infer.rs @@ -333,6 +333,7 @@ impl<'a, D: HirDatabase> InferenceResultBuilder<'a, D> { } else { IntTy::int() }; + Ty::simple(TypeCtor::Int(ty)) } Literal::Float(ty) => { @@ -922,9 +923,9 @@ impl From for ExprOrPatId { mod diagnostics { use crate::diagnostics::{ AccessUnknownField, BreakOutsideLoop, BreakWithValueOutsideLoop, CannotApplyBinaryOp, - ExpectedFunction, FieldCountMismatch, IncompatibleBranch, InvalidLHS, MismatchedStructLit, - MismatchedType, MissingElseBranch, MissingFields, NoFields, NoSuchField, - ParameterCountMismatch, ReturnMissingExpression, + ExpectedFunction, FieldCountMismatch, IncompatibleBranch, InvalidLHS, LiteralOutOfRange, + MismatchedStructLit, MismatchedType, MissingElseBranch, MissingFields, NoFields, + NoSuchField, ParameterCountMismatch, ReturnMissingExpression, }; use crate::{ adt::StructKind, @@ -932,7 +933,7 @@ mod diagnostics { diagnostics::{DiagnosticSink, UnresolvedType, UnresolvedValue}, ty::infer::ExprOrPatId, type_ref::TypeRefId, - ExprId, Function, HirDatabase, Name, Ty, + ExprId, Function, HirDatabase, IntTy, Name, Ty, }; #[derive(Debug, PartialEq, Eq, Clone)] @@ -1011,6 +1012,10 @@ mod diagnostics { id: ExprId, field: usize, }, + LiteralOutOfRange { + id: ExprId, + literal_ty: IntTy, + }, } impl InferenceDiagnostic { @@ -1259,6 +1264,22 @@ mod diagnostics { let field = owner.body_source_map(db).field_syntax(*id, *field).into(); sink.push(NoSuchField { file, field }); } + InferenceDiagnostic::LiteralOutOfRange { id, literal_ty } => { + let literal = body + .expr_syntax(*id) + .expect("could not retrieve expr from source map") + .map(|expr_src| { + expr_src + .left() + .expect("could not retrieve expr from ExprSource") + .cast() + .expect("could not cast expression to literal") + }); + sink.push(LiteralOutOfRange { + literal, + int_ty: *literal_ty, + }) + } } } } diff --git a/crates/mun_hir/src/ty/primitives.rs b/crates/mun_hir/src/ty/primitives.rs index 280a9b382..31c45606c 100644 --- a/crates/mun_hir/src/ty/primitives.rs +++ b/crates/mun_hir/src/ty/primitives.rs @@ -1,5 +1,8 @@ use crate::builtin_type::{BuiltinFloat, BuiltinInt, FloatBitness, IntBitness, Signedness}; +use mun_target::abi; +use mun_target::abi::Integer; use std::fmt::{self}; +use std::{i128, i16, i32, i64, i8, u128, u16, u32, u64, u8}; #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct IntTy { @@ -129,6 +132,40 @@ impl IntTy { (Signedness::Unsigned, IntBitness::X128) => "u128", } } + + /// Returns the maximum positive number that this instance can contain. + pub fn max(self) -> u128 { + match self.signedness { + Signedness::Signed => match self.bitness { + IntBitness::X8 => i8::MAX as u128, + IntBitness::X16 => i16::MAX as u128, + IntBitness::X32 => i32::MAX as u128, + IntBitness::X64 => i64::MAX as u128, + IntBitness::X128 => i128::MAX as u128, + _ => unreachable!("cannot determine max size of variable bitness"), + }, + Signedness::Unsigned => match self.bitness { + IntBitness::X8 => u8::MAX as u128, + IntBitness::X16 => u16::MAX as u128, + IntBitness::X32 => u32::MAX as u128, + IntBitness::X64 => u64::MAX as u128, + IntBitness::X128 => u128::MAX, + _ => unreachable!("cannot determine max size of variable bitness"), + }, + } + } +} + +impl From for IntBitness { + fn from(i: Integer) -> Self { + match i { + Integer::I8 => IntBitness::X8, + Integer::I16 => IntBitness::X16, + Integer::I32 => IntBitness::X32, + Integer::I64 => IntBitness::X64, + Integer::I128 => IntBitness::X128, + } + } } #[derive(Copy, Clone, PartialEq, Eq, Hash)] diff --git a/crates/mun_hir/src/ty/resolve.rs b/crates/mun_hir/src/ty/resolve.rs new file mode 100644 index 000000000..91a001d9b --- /dev/null +++ b/crates/mun_hir/src/ty/resolve.rs @@ -0,0 +1,49 @@ +use super::primitives::IntTy; +use crate::{FloatBitness, FloatTy, IntBitness}; +use mun_target::abi; +use mun_target::abi::TargetDataLayout; + +pub trait ResolveBitness { + /// Resolves any variable bitness into concrete values. + fn resolve(&self, target: &abi::TargetDataLayout) -> Self; +} + +impl ResolveBitness for IntBitness { + fn resolve(&self, data_layout: &abi::TargetDataLayout) -> IntBitness { + match self { + IntBitness::Xsize => data_layout.ptr_sized_integer().into(), + IntBitness::Undefined => IntBitness::X64, + IntBitness::X8 + | IntBitness::X16 + | IntBitness::X32 + | IntBitness::X64 + | IntBitness::X128 => *self, + } + } +} + +impl ResolveBitness for FloatBitness { + fn resolve(&self, _data_layout: &abi::TargetDataLayout) -> FloatBitness { + match self { + FloatBitness::X32 | FloatBitness::X64 => *self, + FloatBitness::Undefined => FloatBitness::X64, + } + } +} + +impl ResolveBitness for IntTy { + fn resolve(&self, target: &TargetDataLayout) -> Self { + IntTy { + bitness: self.bitness.resolve(target), + signedness: self.signedness, + } + } +} + +impl ResolveBitness for FloatTy { + fn resolve(&self, target: &TargetDataLayout) -> Self { + FloatTy { + bitness: self.bitness.resolve(target), + } + } +} diff --git a/crates/mun_hir/src/ty/snapshots/tests__infer_suffix_literals.snap b/crates/mun_hir/src/ty/snapshots/tests__infer_suffix_literals.snap index f9ea7e743..6d1094510 100644 --- a/crates/mun_hir/src/ty/snapshots/tests__infer_suffix_literals.snap +++ b/crates/mun_hir/src/ty/snapshots/tests__infer_suffix_literals.snap @@ -1,6 +1,6 @@ --- source: crates/mun_hir/src/ty/tests.rs -expression: "fn main(){\n 123;\n 123u8;\n 123u16;\n 123u32;\n 123u64;\n 123u128;\n 123uint;\n 1_000_000_u32;\n 123i8;\n 123i16;\n 123i32;\n 123i64;\n 123i128;\n 123int;\n 1_000_000_i32;\n 1_000_123.0e-2;\n 1_000_123.0e-2f32;\n 1_000_123.0e-2f64;\n 1_000_123.0e-2float;\n 9999999999999999999999999999999999999999999_f64;\n}\n\nfn add(a:u32) -> u32 {\n a + 12u32\n}\n\nfn errors() {\n 0b22222; // invalid literal\n 0b00010_f32; // non-10 base float\n 0o71234_f32; // non-10 base float\n 1234_foo; // invalid suffix\n 1234.0_bar; // invalid suffix\n 9999999999999999999999999999999999999999999; // too large\n}" +expression: "fn main(){\n 123;\n 123u8;\n 123u16;\n 123u32;\n 123u64;\n 123u128;\n 123uint;\n 1_000_000_u32;\n 123i8;\n 123i16;\n 123i32;\n 123i64;\n 123i128;\n 123int;\n 1_000_000_i32;\n 1_000_123.0e-2;\n 1_000_123.0e-2f32;\n 1_000_123.0e-2f64;\n 1_000_123.0e-2float;\n 9999999999999999999999999999999999999999999_f64;\n}\n\nfn add(a:u32) -> u32 {\n a + 12u32\n}\n\nfn errors() {\n 0b22222; // invalid literal\n 0b00010_f32; // non-10 base float\n 0o71234_f32; // non-10 base float\n 1234_foo; // invalid suffix\n 1234.0_bar; // invalid suffix\n 9999999999999999999999999999999999999999999; // too large\n 256_u8; // literal out of range for `u8`\n 128_i8; // literal out of range for `i8`\n 12712371237123_u32; // literal out of range `u32`\n 9999999999999999999999999; // literal out of range `int`\n}" --- [408; 415): invalid literal value [440; 451): binary float literal is not supported @@ -8,6 +8,10 @@ expression: "fn main(){\n 123;\n 123u8;\n 123u16;\n 123u32;\n 123 [516; 524): invalid suffix `foo` [548; 558): invalid suffix `bar` [582; 625): int literal is too large +[644; 650): literal out of range for `u8` +[689; 695): literal out of range for `i8` +[734; 752): literal out of range for `u32` +[788; 813): literal out of range for `int` [9; 348) '{ ...f64; }': nothing [15; 18) '123': int [24; 29) '123u8': u8 @@ -34,10 +38,14 @@ expression: "fn main(){\n 123;\n 123u8;\n 123u16;\n 123u32;\n 123 [377; 378) 'a': u32 [377; 386) 'a + 12u32': u32 [381; 386) '12u32': u32 -[402; 641) '{ ...arge }': nothing +[402; 846) '{ ...int` }': nothing [408; 415) '0b22222': int [440; 451) '0b00010_f32': f32 [478; 489) '0o71234_f32': f32 [516; 524) '1234_foo': int [548; 558) '1234.0_bar': float [582; 625) '999999...999999': int +[644; 650) '256_u8': u8 +[689; 695) '128_i8': i8 +[734; 752) '127123...23_u32': u32 +[788; 813) '999999...999999': int diff --git a/crates/mun_hir/src/ty/tests.rs b/crates/mun_hir/src/ty/tests.rs index 972810677..2da3e7d02 100644 --- a/crates/mun_hir/src/ty/tests.rs +++ b/crates/mun_hir/src/ty/tests.rs @@ -46,6 +46,10 @@ fn infer_suffix_literals() { 1234_foo; // invalid suffix 1234.0_bar; // invalid suffix 9999999999999999999999999999999999999999999; // too large + 256_u8; // literal out of range for `u8` + 128_i8; // literal out of range for `i8` + 12712371237123_u32; // literal out of range `u32` + 9999999999999999999999999; // literal out of range `int` } ", )