plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


plum-checker/tests/checker_tests.rs
#![allow(non_snake_case)]
use plum_checker::types::*;
use plum_checker::{checkSource, plumTypeFromAst, unify};
use plum_core::ast::Type as AstType;
use plum_core::{ast::*, AstParser};

fn parse(src: &str) -> Source {
    let mut parser = tree_sitter::Parser::new();
    parser.set_language(&tree_sitter_plum::LANGUAGE.into()).unwrap();
    let tree = parser.parse(src, None).unwrap();
    let ap = AstParser::new(src);
    ap.parseSource(tree.root_node())
}

#[test]
fn astTypeIntMapsToTint() {
    let ast_ty = AstType { name: "Int".to_string(), generics: vec![] };
    assert_eq!(plumTypeFromAst(&ast_ty), PlumType::TInt);
}

#[test]
fn astTypeUnknownMapsToNamed() {
    let ast_ty = AstType { name: "MyClass".to_string(), generics: vec![] };
    assert_eq!(plumTypeFromAst(&ast_ty), PlumType::TNamed("MyClass".to_string()));
}

#[test]
fn unifySameTypesOk() {
    assert!(unify(&PlumType::TInt, &PlumType::TInt).is_ok());
    assert!(unify(&PlumType::TFloat, &PlumType::TFloat).is_ok());
}

#[test]
fn unifyDifferentTypesErr() {
    assert!(unify(&PlumType::TInt, &PlumType::TFloat).is_err());
}

#[test]
fn freshVarsAreUnique() {
    let mut state = InferState::new();
    let a = state.freshVar();
    let b = state.freshVar();
    assert_ne!(a, b);
    assert_eq!(a, "a0");
    assert_eq!(b, "a1");
}

#[test]
fn monoScheme() {
    let scheme = TypeScheme::mono(PlumType::TInt);
    assert!(scheme.vars.is_empty());
    assert_eq!(*scheme.body, PlumType::TInt);
}

#[test]
fn validAddFnPasses() {
    let src = "fun add(a: Int, b: Int) -> Int =\n  a + b\n";
    let source = parse(src);
    assert!(checkSource(&source).is_ok(), "expected Ok");
}

#[test]
fn wrongReturnTypeIsError() {
    let src = "fun bad() -> Int =\n  True\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
    let errs = result.unwrap_err();
    assert!(errs[0].message.contains("return type mismatch"), "got: {}", errs[0].message);
}

#[test]
fn undeclaredVarIsError() {
    let src = "fun bad() -> Int =\n  x\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn typeMismatchInBinaryOpIsError() {
    let src = "fun bad() -> Int =\n  1 + 2.0\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn boolLiteralTrueFalseAreBool() {
    let src = "fun isTrue() -> Bool =\n  True\n";
    let source = parse(src);
    assert!(checkSource(&source).is_ok(), "expected Ok, got {:?}", checkSource(&source).err());
}

#[test]
fn boolLiteralWrongReturnTypeIsError() {
    let src = "fun bad() -> Int =\n  False\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn methodSelfFieldAccessPasses() {
    let src = "type Cat =\n  name: Str\n  age: Int\n\n  fun getName() -> Str =\n    self.name\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn methodSelfUnknownFieldIsError() {
    let src = "type Cat =\n  name: Str\n\n  fun getAge() -> Int =\n    self.age\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn nestedMethodTypeChecks() {
    let src = "type Cat =\n  name: Str\n  age: Int\n\n  fun getName(self) -> Str =\n    self.name\n";
    let result = checkSource(&parse(src));
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn nestedMethodUnknownFieldIsError() {
    let src = "type Cat =\n  name: Str\n\n  fun getAge(self) -> Int =\n    self.age\n";
    assert!(checkSource(&parse(src)).is_err());
}

#[test]
fn selfOutsideMethodIsError() {
    let src = "fun bad() -> Int =\n  self\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn classCallChecksFieldTypes() {
    let src = "type Cat =\n  name: Str\n  age: Int\n\nfun makeCat() -> Cat =\n  Cat(name: \"x\", age: 1)\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn classCallWrongFieldTypeIsError() {
    let src = "type Cat =\n  name: Str\n  age: Int\n\nfun makeCat() -> Cat =\n  Cat(name: \"x\", age: \"y\")\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn classCallUnknownFieldIsError() {
    let src = "type Cat =\n  name: Str\n\nfun makeCat() -> Cat =\n  Cat(name: \"x\", age: 1)\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn methodCallViaAttributeTypeChecksArgs() {
    let src = "type Cat =\n  name: Str\n\n  fun rename(n: Str) -> Str =\n    n\n\nfun use(c: Cat) -> Str =\n  c.rename(\"x\")\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn matchBindsNamePatternToSubjectType() {
    let src = "fun main(a: Int) -> Int =\n  match a\n    x =>\n      x\n    _ =>\n      0\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn matchInlineCaseBodyTypeChecks() {
    // Case bodies can be a single inline expression, not just an indented block.
    let src = "fun main(a: Int) -> Int =\n  match a\n    1 => 10\n    _ => 0\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn matchTrueFalseAreVariantPatternsNotBindingsWithoutEnumDecl() {
    // True/False are built-in Bool variants — they must be recognized as tag
    // comparisons even when the source doesn't redeclare `enum Bool`, so a
    // later `_` wildcard arm remains reachable (each pattern binds/compares,
    // it doesn't just re-bind the subject under the name "True").
    let src = "fun pick(a: Bool) -> Int =\n  match a\n    True =>\n      1\n    False =>\n      0\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn matchIntPatternAgainstStrSubjectIsError() {
    let src = "fun main(a: Str) -> Int =\n  match a\n    1 =>\n      1\n    _ =>\n      0\n";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn bareEnumTagUnifiesWithOwningEnumType() {
    // Regression: a bare non-Bool tag like `None` used to type as `TNamed("None")`
    // (itself, not its enum), so comparing it against an `Option` value would wrongly
    // fail with a type mismatch.
    let src = "\
enum Option =
  | Some[Int]
  | None

fun isNone(o: Option) -> Bool = o == None
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn variantConstructionChecksArgCountAndTypes() {
    let src = "\
enum Option =
  | Some[Int]
  | None

fun makeSome(v: Int) -> Option =
  Some(v)
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn variantConstructionWrongArgTypeIsError() {
    let src = "\
enum Option =
  | Some(Int)
  | None

fun bad() -> Option =
  Some(\"x\")
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn variantConstructionWrongArgCountIsError() {
    let src = "\
enum Shape =
  | Rect[Float, Float]
  | Circle[Float]

fun bad() -> Shape =
  Rect(1.0)
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn enumDiscriminantValuesTypeCheckWithNoErrors() {
    let src = "\
enum Step(n: Int) =
  | ReadMin(0)
  | ReadMax(1)
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn enumDiscriminantWrongValueCountIsError() {
    let src = "\
enum Step(n: Int) =
  | ReadMin(0)
  | ReadMax
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn enumDiscriminantWronglyTypedValueIsError() {
    let src = "\
enum Step(n: Int) =
  | ReadMin(0)
  | ReadMax(\"x\")
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn enumDiscriminantValueOnParamLessEnumIsError() {
    let src = "\
enum Option =
  | Some(5)
  | None
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn fieldAccessOnDiscriminantEnumReceiverTypeChecks() {
    let src = "\
enum Step(n: Int) =
  | ReadMin(0)
  | ReadMax(1)

  fun toNumber(self) -> Int =
    self.n
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn fieldAccessOnOrdinaryEnumReceiverIsUnaffectedByEnumParams() {
    // An ordinary enum (no discriminant `params`) isn't in `ctx.classes` OR
    // `ctx.enum_params`, so it falls through to the same permissive "unmodeled
    // type" escape hatch every other type not in `ctx.classes` gets (codegen,
    // not the checker, is what would catch a genuinely bad field access here) —
    // exactly as it did before discriminant enums existed.
    let src = "\
enum Option =
  | Some[Int]
  | None

  fun bad(self) -> Int =
    self.n
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn constructorPatternBindsFieldsToDeclaredTypes() {
    let src = "\
enum Shape =
  | Rect[Float, Float]
  | Circle[Float]

fun area(s: Shape) -> Float =
  match s
    Rect(w, h) =>
      w * h
    Circle(r) =>
      r * r
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn constructorPatternWrongFieldCountIsError() {
    let src = "\
enum Shape =
  | Rect[Float, Float]
  | Circle[Float]

fun bad(s: Shape) -> Float =
  match s
    Rect(w) =>
      w
    _ =>
      0.0
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn classNameCollidingWithEnumVariantIsAClearError() {
    // `Cat(...)` is ambiguous when `Cat` is both a class and an enum variant:
    // downstream code consults `enum_variants` first, so the class
    // constructor would otherwise be silently shadowed with no diagnostic.
    let src = "\
type Cat =
  name: Str

enum Animal =
  | Cat
  | Dog
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err(), "expected Err");
    let errs = result.unwrap_err();
    assert!(
        errs.iter().any(|e| e.message.contains("is declared as both a class and an enum variant")),
        "got: {:?}",
        errs
    );
}

#[test]
fn genericClassInstantiatedAtTwoConcreteTypesTypeChecks() {
    let src = "\
type Box[T] =
  value: T

fun makeIntBox() -> Box =
  Box(value: 5)

fun makeStrBox() -> Box =
  Box(value: \"x\")
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn genericFunctionCalledWithDifferentConcreteTypesPerSiteTypeChecks() {
    let src = "\
fun wrap(value: T) -> Bool =
  True

fun useInt() -> Bool =
  wrap(5)

fun useStr() -> Bool =
  wrap(\"x\")
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn genericFunctionWithTwoIndependentTypeParamsTypeChecks() {
    let src = "\
fun pair(first: T, second: U) -> Bool =
  True

fun use() -> Bool =
  pair(1, \"x\")
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn genericFunctionWithInternallyInconsistentBodyIsRejectedAfterSpecialization() {
    // A generic function whose body is genuinely inconsistent with its concrete,
    // correct declared return type must still be REJECTED after specialization.
    // The `-> Str` on `useIt` means the buggy (unconditional return-overwrite)
    // behavior — rewriting `wrong$Str`'s `-> Int` to `-> Str` — would make the
    // whole program type-check, masking the real `expected Int, found Str` error.
    let src = "\
fun wrong(x: T) -> Int =
  \"hello\"

fun useIt() -> Str =
  wrong(\"s\")
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(
        result.is_err(),
        "expected the internally-inconsistent generic function to be rejected, got Ok"
    );
}

#[test]
fn genericEnumSingleInstantiationTypeChecks() {
    // A generic Option-shaped enum, constructed at one concrete type (`Some(5)`),
    // matched, must resolve end-to-end via checkSource.
    let src = "\
enum Option =
  | Some[T]
  | None

fun get() -> Int =
  o = Some(5)
  match o
    Some(v) =>
      v
    None =>
      0
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());

    // Directly prove resolution happened: the monomorphized output must contain a
    // concrete `Option$Int` enum whose `Some$Int` variant carries an `Int` field
    // (not the generic `a`), and must NOT retain the generic `Option` template.
    // The variant name is ALSO mangled (`Some` -> `Some$Int`), the same suffix as
    // the enum's own name.
    let mono = plum_checker::monomorphize::monomorphizeSource(&source)
        .expect("monomorphize should succeed");
    let opt = mono.items.iter().find_map(|it| match it {
        Item::Enum(e) if e.name == "Option$Int" => Some(e),
        _ => None,
    });
    let opt = opt.expect("expected a specialized `Option$Int` enum in the output");
    let some = opt.variants.iter().find(|v| v.name == "Some$Int")
        .expect("expected `Some$Int` (mangled) variant on `Option$Int`");
    assert_eq!(some.fields, vec!["Int".to_string()], "Some's field should be concrete Int");
    assert!(
        !mono.items.iter().any(|it| matches!(it, Item::Enum(e) if e.name == "Option")),
        "the generic `Option` template must be dropped from the output"
    );
}

#[test]
fn genericEnumMultipleInstantiationsCoexistAndTypeCheck() {
    // The SAME generic enum instantiated at two different concrete types in one
    // program must now type-check correctly for BOTH instantiations — this is the
    // behavior this task adds (previously this was a documented, rejected limitation).
    let src = "\
enum Option =
  | Some[T]
  | None

fun useInt() -> Int =
  o = Some(5)
  match o
    Some(v) =>
      v
    None =>
      0

fun useStr() -> Str =
  o = Some(\"x\")
  match o
    Some(v) =>
      v
    None =>
      \"z\"
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());

    // Directly prove both specializations exist independently, with distinct
    // mangled variant names, so neither collides with the other.
    let mono = plum_checker::monomorphize::monomorphizeSource(&source)
        .expect("monomorphize should succeed");
    let has_enum_with_variant = |enum_name: &str, variant_name: &str| {
        mono.items.iter().any(|it| matches!(it, Item::Enum(e) if e.name == enum_name
            && e.variants.iter().any(|v| v.name == variant_name)))
    };
    assert!(has_enum_with_variant("Option$Int", "Some$Int"), "expected Option$Int with Some$Int");
    assert!(has_enum_with_variant("Option$Str", "Some$Str"), "expected Option$Str with Some$Str");
}

#[test]
fn genericMethodOnGenericClassTypeChecks() {
    let src = "\
type Box[T] =
  value: T

  fun getValue() -> T =
    self.value

fun use() -> Int =
  b = Box(value: 5)
  b.getValue()
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
#[ignore = "slow (~100s) — exercises the 10,000-specialization runaway-recursion guard; run explicitly with --ignored when touching monomorphize.rs's guard logic"]
fn unboundedRecursiveGenericInstantiationIsAClearError() {
    // `recurse` re-wraps its argument in a `Box` on every recursive call, so each
    // specialization's own body demands a specialization of `recurse` at a STRICTLY
    // bigger type (`recurse$Int`, then `recurse$Box$Int`, then `recurse$Box$Box$Int`,
    // ...), forever. This genuinely grows the worklist without bound (unlike a
    // generic class field merely NAMING a recursive generic type in its own
    // declaration, which is never itself a call site and so never reaches the
    // worklist at all) and must fail with a clear, bounded error rather than hang.
    let src = "\
type Box[T] =
  value: T

fun recurse(v: T) -> Int =
  b = Box(value: v)
  recurse(b)

fun use() -> Int =
  recurse(5)
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
    let errs = result.unwrap_err();
    assert!(errs[0].message.contains("monomorphize"), "got: {:?}", errs);
}




#[test]
fn ordinaryFunctionWithBareGenericEnumParamTypeChecks() {
    // The shape that broke the pre-existing codegen test: an otherwise-ordinary
    // function taking a bare generic-enum-typed parameter.
    let src = "\
enum Option =
  | Some[T]
  | None

fun unwrapOr(o: Option, default: Int) -> Int =
  match o
    Some(v) =>
      v
    None =>
      default

fun use() -> Int =
  unwrapOr(Some(5), 0)
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());

    // Directly prove `unwrapOr` itself got specialized (not left bare/unresolved).
    let mono = plum_checker::monomorphize::monomorphizeSource(&source)
        .expect("monomorphize should succeed");
    let has_specialized_unwrap_or = mono.items.iter().any(|it| matches!(it, Item::Fn(f)
        if f.name.starts_with("unwrapOr$") && f.type_param.is_none()));
    assert!(has_specialized_unwrap_or, "expected a specialized `unwrapOr$...` function in the output");
}

#[test]
fn ordinaryFunctionWithBareGenericClassParamTypeChecks() {
    // The same shape, for a generic CLASS param instead of an enum — untested until
    // now, but the identical root cause: `Box` is dropped from the monomorphized
    // output, so a bare `Box`-typed param would otherwise reference nothing.
    let src = "\
type Box[T] =
  value: T

  fun getBoxValue() -> T =
    self.value

fun sumBox(b: Box) -> Int =
  b.getBoxValue()

fun use() -> Int =
  sumBox(Box(value: 5))
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn closureLiteralInfersAsAFunctionType() {
    let src = "\
fun useClosure() -> Bool =
  cb = |v|
    True
  cb(5)
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn fnValueTypedParamCanBeCalled() {
    let src = "\
fun each(cb: fn(Int) -> Bool) -> Bool =
  cb(5)
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn fieldAssignmentTargetWithMatchingTypePasses() {
    let src = "\
type Cat =
  name: Str
  age: Int

  fun haveBirthday() =
    self.age = self.age + 1
";
    let source = parse(src);
    assert!(checkSource(&source).is_ok(), "expected Ok");
}

#[test]
fn fieldAssignmentTargetWithMismatchedTypeIsError() {
    let src = "\
type Cat =
  name: Str
  age: Int

  fun breakCat() =
    self.age = \"oops\"
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn fieldAssignmentTargetUnknownFieldIsError() {
    let src = "\
type Cat =
  name: Str
  age: Int

  fun breakCat() =
    self.nope = 1
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_err());
}

#[test]
fn closurePassedToFnValueTypedParamTypeChecks() {
    let src = "\
fun each(cb: fn(Int) -> Bool) -> Bool =
  cb(5)

fun use() -> Bool =
  each(|v|
    True)
";
    let source = parse(src);
    let result = checkSource(&source);
    assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
}

#[test]
fn variadicCallWithZeroTrailingArgsPasses() {
    let src = "\
fun sumAll(nums: ...Int) -> Int =
  0

fun useSumAll() -> Int =
  sumAll()
";
    let source = parse(src);
    assert!(checkSource(&source).is_ok(), "expected Ok");
}

#[test]
fn variadicCallWithSeveralTrailingArgsPasses() {
    let src = "\
fun sumAll(nums: ...Int) -> Int =
  0

fun useSumAll() -> Int =
  sumAll(1, 2, 3)
";
    let source = parse(src);
    assert!(checkSource(&source).is_ok(), "expected Ok");
}

#[test]
fn variadicCallWithMismatchedTrailingArgTypeIsError() {
    let src = "\
fun sumAll(nums: ...Int) -> Int =
  0

fun useSumAll() -> Int =
  sumAll(1, \"two\")
";
    let source = parse(src);
    assert!(checkSource(&source).is_err());
}

#[test]
fn variadicCallWithFixedPrefixPasses() {
    let src = "\
fun combine(prefix: Int, rest: ...Int) -> Int =
  prefix

fun useCombine() -> Int =
  combine(1, 2, 3)
";
    let source = parse(src);
    assert!(checkSource(&source).is_ok(), "expected Ok");
}

#[test]
fn twoVariadicParamsIsError() {
    let src = "\
fun bad(a: ...Int, b: ...Int) -> Int =
  0
";
    let source = parse(src);
    assert!(checkSource(&source).is_err());
}

#[test]
fn variadicParamNotLastIsError() {
    let src = "\
fun bad(a: ...Int, b: Int) -> Int =
  0
";
    let source = parse(src);
    assert!(checkSource(&source).is_err());
}

#[test]
fn forLoopOverVariadicBindsElementType() {
    let src = "\
fun sumAll(nums: ...Int) -> Int =
  total = 0
  for v := range nums
    total = total + v
  total
";
    let source = parse(src);
    assert!(checkSource(&source).is_ok(), "expected Ok");
}

#[test]
fn forLoopOverVariadicWithTwoVarsIsError() {
    let src = "\
fun bad(nums: ...Int) -> Int =
  for v, i := range nums
    v
  0
";
    let source = parse(src);
    assert!(checkSource(&source).is_err());
}