Skip to content

Commit

Permalink
wip: add diagnostics for cyclic types
Browse files Browse the repository at this point in the history
  • Loading branch information
sinato committed Aug 26, 2020
1 parent aebe7b3 commit 44ae87d
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 18 deletions.
5 changes: 5 additions & 0 deletions crates/mun_compiler/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,9 @@ mod tests {
fn test_type_alias_target_undeclared_error() {
insta::assert_display_snapshot!(compilation_errors("\n\ntype Foo = UnknownType;"));
}

#[test]
fn test_cyclic_type_alias_error() {
insta::assert_display_snapshot!(compilation_errors("\n\ntype Foo = Foo;"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
source: crates/mun_compiler/src/diagnostics.rs
expression: "compilation_errors(\"\\n\\ntype Foo = Foo;\")"
---
error: cyclic type
--> main.mun:3:12
|
3 | type Foo = Foo;
| ^^^ cyclic type
|
6 changes: 4 additions & 2 deletions crates/mun_hir/src/code_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,8 @@ impl Function {
}

pub fn ty(self, db: &dyn HirDatabase) -> Ty {
db.type_for_def(self.into(), Namespace::Values)
// TODO: Add detection of cyclick types
db.type_for_def(self.into(), Namespace::Values).0
}

pub fn infer(self, db: &dyn HirDatabase) -> Arc<InferenceResult> {
Expand Down Expand Up @@ -424,7 +425,8 @@ impl Struct {
}

pub fn ty(self, db: &dyn HirDatabase) -> Ty {
db.type_for_def(self.into(), Namespace::Types)
// TODO: Add detection of cyclick types
db.type_for_def(self.into(), Namespace::Types).0
}

pub fn lower(self, db: &dyn HirDatabase) -> Arc<LowerBatchResult> {
Expand Down
2 changes: 1 addition & 1 deletion crates/mun_hir/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {

#[salsa::invoke(crate::ty::type_for_def)]
#[salsa::cycle(crate::ty::type_for_cycle_recover)]
fn type_for_def(&self, def: TypableDef, ns: Namespace) -> Ty;
fn type_for_def(&self, def: TypableDef, ns: Namespace) -> (Ty, bool);

#[salsa::invoke(crate::expr::body_hir_query)]
fn body(&self, def: DefWithBody) -> Arc<crate::expr::Body>;
Expand Down
20 changes: 20 additions & 0 deletions crates/mun_hir/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,26 @@ impl Diagnostic for UnresolvedType {
}
}

#[derive(Debug)]
pub struct CyclicType {
pub file: FileId,
pub type_ref: AstPtr<ast::TypeRef>,
}

impl Diagnostic for CyclicType {
fn message(&self) -> String {
"cyclic type".to_string()
}

fn source(&self) -> InFile<SyntaxNodePtr> {
InFile::new(self.file, self.type_ref.syntax_node_ptr())
}

fn as_any(&self) -> &(dyn Any + Send + 'static) {
self
}
}

#[derive(Debug)]
pub struct ExpectedFunction {
pub file: FileId,
Expand Down
13 changes: 11 additions & 2 deletions crates/mun_hir/src/ty/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ impl<'a> InferenceResultBuilder<'a> {
LowerDiagnostic::UnresolvedType { id } => {
InferenceDiagnostic::UnresolvedType { id }
}
LowerDiagnostic::CyclicType { id } => InferenceDiagnostic::CyclicType { id },
};
self.diagnostics.push(diag);
}
Expand Down Expand Up @@ -700,7 +701,8 @@ impl<'a> InferenceResultBuilder<'a> {
Resolution::Def(def) => {
let typable: Option<TypableDef> = def.into();
let typable = typable?;
let ty = self.db.type_for_def(typable, Namespace::Values);
// TODO: Add detection of cyclick types
let (ty, _) = self.db.type_for_def(typable, Namespace::Values);
if check_params.is_unit_struct {
if let Some(s) = ty.as_struct() {
self.check_unit_struct_lit(id, s);
Expand Down Expand Up @@ -1007,7 +1009,7 @@ mod diagnostics {
use crate::{
adt::StructKind,
code_model::src::HasSource,
diagnostics::{DiagnosticSink, UnresolvedType, UnresolvedValue},
diagnostics::{CyclicType, DiagnosticSink, UnresolvedType, UnresolvedValue},
ty::infer::ExprOrPatId,
type_ref::TypeRefId,
ExprId, Function, HirDatabase, IntTy, Name, Ty,
Expand All @@ -1021,6 +1023,9 @@ mod diagnostics {
UnresolvedType {
id: TypeRefId,
},
CyclicType {
id: TypeRefId,
},
ExpectedFunction {
id: ExprId,
found: Ty,
Expand Down Expand Up @@ -1128,6 +1133,10 @@ mod diagnostics {
let type_ref = body.type_ref_syntax(*id).expect("If this is not found, it must be a type ref generated by the library which should never be unresolved.");
sink.push(UnresolvedType { file, type_ref });
}
InferenceDiagnostic::CyclicType { id } => {
let type_ref = body.type_ref_syntax(*id).expect("If this is not found, it must be a type ref generated by the library which should never be unresolved.");
sink.push(CyclicType { file, type_ref });
}
InferenceDiagnostic::ParameterCountMismatch {
id,
expected,
Expand Down
34 changes: 21 additions & 13 deletions crates/mun_hir/src/ty/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,14 @@ impl Ty {
) -> Ty {
let res = match &type_ref_map[type_ref] {
TypeRef::Path(path) => Ty::from_hir_path(db, resolver, path),
TypeRef::Error => Some(Ty::Unknown),
TypeRef::Empty => Some(Ty::Empty),
TypeRef::Never => Some(Ty::simple(TypeCtor::Never)),
TypeRef::Error => Some((Ty::Unknown, false)),
TypeRef::Empty => Some((Ty::Empty, false)),
TypeRef::Never => Some((Ty::simple(TypeCtor::Never), false)),
};
if let Some(ty) = res {
if let Some((ty, is_cycric)) = res {

This comment has been minimized.

Copy link
@Wodann

Wodann Aug 27, 2020

Collaborator

a typo: is_cyclic

if is_cycric {
diagnostics.push(LowerDiagnostic::CyclicType { id: type_ref })
}
ty
} else {
diagnostics.push(LowerDiagnostic::UnresolvedType { id: type_ref });
Expand All @@ -83,7 +86,7 @@ impl Ty {
db: &dyn HirDatabase,
resolver: &Resolver,
path: &Path,
) -> Option<Self> {
) -> Option<(Self, bool)> {
let resolution = resolver
.resolve_path_without_assoc_items(db, path)
.take_types();
Expand All @@ -102,8 +105,7 @@ impl Ty {
Some(it) => it,
};

let ty = db.type_for_def(typable, Namespace::Types);
Some(ty)
Some(db.type_for_def(typable, Namespace::Types))
}
}

Expand Down Expand Up @@ -198,8 +200,8 @@ impl CallableDef {
/// `struct Foo(usize)`, we have two types: The type of the struct itself, and
/// the constructor function `(usize) -> Foo` which lives in the values
/// namespace.
pub(crate) fn type_for_def(db: &dyn HirDatabase, def: TypableDef, ns: Namespace) -> Ty {
match (def, ns) {
pub(crate) fn type_for_def(db: &dyn HirDatabase, def: TypableDef, ns: Namespace) -> (Ty, bool) {
let ty = match (def, ns) {
(TypableDef::Function(f), Namespace::Values) => type_for_fn(db, f),
(TypableDef::BuiltinType(t), Namespace::Types) => type_for_builtin(t),
(TypableDef::Struct(s), Namespace::Values) => type_for_struct_constructor(db, s),
Expand All @@ -210,7 +212,8 @@ pub(crate) fn type_for_def(db: &dyn HirDatabase, def: TypableDef, ns: Namespace)
(TypableDef::Function(_), Namespace::Types) => Ty::Unknown,
(TypableDef::BuiltinType(_), Namespace::Values) => Ty::Unknown,
(TypableDef::TypeAlias(_), Namespace::Values) => Ty::Unknown,
}
};
(ty, false)
}

/// Recover with an unknown type when a cycle is detected in the salsa database.
Expand All @@ -219,8 +222,8 @@ pub(crate) fn type_for_cycle_recover(
_cycle: &[String],
_def: &TypableDef,
_ns: &Namespace,
) -> Ty {
Ty::Unknown
) -> (Ty, bool) {
(Ty::Unknown, true)
}

/// Build the declared type of a static.
Expand Down Expand Up @@ -291,7 +294,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, def: TypeAlias) -> Ty {
}

pub mod diagnostics {
use crate::diagnostics::UnresolvedType;
use crate::diagnostics::{CyclicType, UnresolvedType};
use crate::{
diagnostics::DiagnosticSink,
type_ref::{TypeRefId, TypeRefSourceMap},
Expand All @@ -301,6 +304,7 @@ pub mod diagnostics {
#[derive(Debug, PartialEq, Eq, Clone)]
pub(crate) enum LowerDiagnostic {
UnresolvedType { id: TypeRefId },
CyclicType { id: TypeRefId },
}

impl LowerDiagnostic {
Expand All @@ -316,6 +320,10 @@ pub mod diagnostics {
file: file_id,
type_ref: source_map.type_ref_syntax(*id).unwrap(),
}),
LowerDiagnostic::CyclicType { id } => sink.push(CyclicType {
file: file_id,
type_ref: source_map.type_ref_syntax(*id).unwrap(),
}),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
source: crates/mun_hir/src/ty/tests.rs
expression: "struct Foo {}\ntype Foo = Foo;\n\ntype A = B;\ntype B = A;\n\nfn main() {\n let a: Foo; // error: unknown type\n let b: A; // error: unknown type\n let c: B; // error: unknown type\n}"
---
[25; 28): cyclic type
[40; 41): cyclic type
[52; 53): cyclic type
[79; 82): cyclic type
[119; 120): cyclic type
[159; 160): cyclic type
[66; 189) '{ ...type }': nothing
[76; 77) 'a': {unknown}
[116; 117) 'b': {unknown}
Expand Down

0 comments on commit 44ae87d

Please sign in to comment.