A reasonable configuration language rcl-lang.org
configuration-language json
at master 172 lines 6.8 kB view raw
1// RCL -- A reasonable configuration language. 2// Copyright 2024 Ruud van Asseldonk 3 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// A copy of the License has been included in the root of the repository. 7 8//! A type diff is the result of a subtype check. 9//! 10//! This module contains the definitions, and machinery for printing type diffs. 11use crate::error::{IntoError, Result}; 12use crate::pprint::{concat, indent, Doc}; 13use crate::source::Span; 14use crate::types::{AsTypeName, FunctionArg, Side, SourcedType, Type}; 15 16/// A component in a subtype check `T ≤ U` where `U` is expected and `T` encountered. 17#[derive(Debug)] 18pub enum Mismatch { 19 /// The type error cannot be broken down further. Here are `T` and `U`. 20 Atom { 21 expected: SourcedType, 22 actual: SourcedType, 23 }, 24 25 /// Both sides are a list, but the element type has an issue. 26 List(Box<TypeDiff<SourcedType>>), 27 28 /// Both sides are a set, but the element type has an issue. 29 Set(Box<TypeDiff<SourcedType>>), 30 31 /// Both sides are a dict, but the key or value (or both) have issues. 32 Dict(Box<TypeDiff<SourcedType>>, Box<TypeDiff<SourcedType>>), 33 34 /// Both sides are functions of the same arity, but args or result have issues. 35 Function(Vec<TypeDiff<FunctionArg>>, Box<TypeDiff<SourcedType>>), 36} 37 38/// The result of a subtype check `T ≤ U` where `U` is expected and `T` encountered. 39/// 40/// The result is a tree, to be able to pinpoint where the check fails, enabling 41/// more helpful errors. 42/// 43/// See also [`SourcedType::is_subtype_of`]. 44#[derive(Debug)] 45pub enum TypeDiff<T> { 46 /// Yes, `T ≤ U`, and here is `T`. 47 Ok(T), 48 49 /// For `t: T`, we *might* have `t: U`. Here is `V` such that `T ≤ V ≤ U`. 50 Defer(T), 51 52 /// For all `t: T`, we have that `t` is not a value of `U`. 53 /// 54 /// Or, in some cases this is not strictly true, but we want to rule out 55 /// that case because it makes more sense. For example, we say that 56 /// `List[Number]` and `List[String]` are incompatible, even though `[]` 57 /// inhabits both. 58 Error(Mismatch), 59} 60 61/// The result of a static typecheck. 62pub enum Typed<T> { 63 /// The type is known statically, and this is the most specific type we infer. 64 Type(T), 65 66 /// We can't check this statically, a runtime check is needed. 67 /// 68 /// If the runtime check passes, then the value fits the returned type. 69 Defer(T), 70} 71 72impl<T> TypeDiff<T> { 73 pub fn check(self, at: Span) -> Result<Typed<T>> { 74 self.check_with_context(at, "") 75 } 76 77 pub fn check_unpack_scalar(self, at: Span) -> Result<Typed<T>> { 78 self.check_with_context(at, " in unpacked element") 79 } 80 81 /// Report the diff as a type error, or extract its result. 82 fn check_with_context(self, at: Span, location_context: &'static str) -> Result<Typed<T>> { 83 match self { 84 TypeDiff::Ok(t) => Ok(Typed::Type(t)), 85 TypeDiff::Defer(t) => Ok(Typed::Defer(t)), 86 TypeDiff::Error(Mismatch::Atom { actual, expected }) => { 87 let mut error = if let Type::Void = expected.type_ { 88 at.error(concat! { 89 "Expected a value of type " 90 "Void".format_type() 91 location_context 92 ", but no such values exist." 93 }) 94 } else { 95 // Any can never be the top level-cause of a type error. 96 // As a supertype, any value is fine, and as the actual type, 97 // it should result in a runtime check rather than an error. 98 debug_assert_ne!(actual.type_, Type::Any, "Any should not cause errors."); 99 debug_assert_ne!(expected.type_, Type::Any, "Any should not cause errors."); 100 101 // A top-level type error, we can report with a simple message. 102 at.error(concat! { 103 "Type mismatch" 104 location_context 105 "." 106 }) 107 .with_body(report_type_mismatch(&expected, &actual)) 108 }; 109 110 // If we have it, explain why the expected type is expected. 111 expected.explain_error(Side::Expected, &mut error); 112 113 // If the actual type doesn't come from the span that we are 114 // attributing the error to, then also include a note about that. 115 let should_report_source = match actual.source.span() { 116 Some(src_span) => at != src_span, 117 None => true, 118 }; 119 if should_report_source { 120 actual.explain_error(Side::Actual, &mut error); 121 } 122 error.err() 123 } 124 TypeDiff::Error(diff) => { 125 // If the error is nested somewhere inside a type, then we 126 // resort to a more complex format where we first print the 127 // type itself, with the error part replaced with a placeholder, 128 // and then we add a secondary error to explain the placeholder. 129 crate::fmt_type::DiffFormatter::report(at, location_context, &diff).err() 130 } 131 } 132 } 133} 134 135/// Format a static type error body. 136/// 137/// This does not include the "Type mismatch." message, so that the body can be 138/// used in various places. 139pub fn report_type_mismatch<T1: AsTypeName, T2: AsTypeName>( 140 expected: &T1, 141 actual: &T2, 142) -> Doc<'static> { 143 // If types are atoms, they are short to format, so we can put the message 144 // on one line. If they are composite, we put them in an indented block. 145 match (expected.is_atom(), actual.is_atom()) { 146 (true, true) => concat! { 147 "Expected " expected.format_type() 148 " but found " actual.format_type() "." 149 }, 150 (true, false) => concat! { 151 "Expected " expected.format_type() " but found this type:" 152 Doc::HardBreak Doc::HardBreak 153 indent! { actual.format_type() } 154 }, 155 (false, true) => concat! { 156 "Expected this type:" 157 Doc::HardBreak Doc::HardBreak 158 indent! { expected.format_type() } 159 Doc::HardBreak Doc::HardBreak 160 "But found " actual.format_type() "." 161 }, 162 (false, false) => concat! { 163 "Expected this type:" 164 Doc::HardBreak Doc::HardBreak 165 indent! { expected.format_type() } 166 Doc::HardBreak Doc::HardBreak 167 "But found this type: " 168 Doc::HardBreak Doc::HardBreak 169 indent! { actual.format_type() } 170 }, 171 } 172}