forked from extern/nushell
Relax type-check of key-less table
/record
(#10629)
# Description Relax typechecking of key-less `table`/`record` Assume that they are acceptable for more narrowly specified `table<...>`/`record<...>` where `...` specifies keys and potentially types for those keys/columns. This ensures that you can use commands that specify general return values statically with more specific input-/args-type requirements. Reduces the power of the type-check a bit but unlocks you to actually use the specific annotations in more places. Incompatibilities will only be raised if an output type declares specific columns/keys. Closes #9702 Supersedes #10594 as a simpler solution requiring no extra distinction. h/t @1kinoti, @NotLebedev # User-Facing Changes Now legal at type-check time ```nu def foo []: nothing -> table { [] } def foo []: nothing -> table<> { ls } def bar []: table<a:int,b:string> -> nothing {} foo | bar ``` # Tests + Formatting - 1 explicit test with specified relaxed return type passed to concrete expected input type - 1 test leveraging the general output type of a built-in command - 1 test wrapping a general built-in command and verifying the type inference in the function body
This commit is contained in:
parent
ff6c0fcb81
commit
e427c68731
@ -10,7 +10,9 @@ use nu_protocol::{
|
|||||||
pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {
|
pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {
|
||||||
// Structural subtyping
|
// Structural subtyping
|
||||||
let is_compatible = |expected: &[(String, Type)], found: &[(String, Type)]| {
|
let is_compatible = |expected: &[(String, Type)], found: &[(String, Type)]| {
|
||||||
if expected.is_empty() {
|
if expected.is_empty() || found.is_empty() {
|
||||||
|
// We treat an incoming empty table/record type as compatible for typechecking purposes
|
||||||
|
// It is the responsibility of the runtime to reject if necessary
|
||||||
true
|
true
|
||||||
} else if expected.len() > found.len() {
|
} else if expected.len() > found.len() {
|
||||||
false
|
false
|
||||||
|
@ -87,6 +87,33 @@ fn record_subtyping_3() -> TestResult {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_subtyping_allows_general_record() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
"def test []: record<name: string, age: int> -> string { $in; echo 'success' };
|
||||||
|
def underspecified []: nothing -> record {{name:'Douglas', age:42}};
|
||||||
|
underspecified | test",
|
||||||
|
"success",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_subtyping_allows_record_after_general_command() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
"def test []: record<name: string, age: int> -> string { $in; echo 'success' };
|
||||||
|
{name:'Douglas', surname:'Adams', age:42} | select name age | test",
|
||||||
|
"success",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_subtyping_allows_general_inner() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
"def merge_records [other: record<bar: int>]: record<foo: string> -> record<foo: string, bar: int> { merge $other }",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transpose_into_load_env() -> TestResult {
|
fn transpose_into_load_env() -> TestResult {
|
||||||
run_test(
|
run_test(
|
||||||
|
Loading…
Reference in New Issue
Block a user