From 3b9c808be4966110584770f782d4d9ef8cce60f0 Mon Sep 17 00:00:00 2001 From: OceanOak Date: Thu, 25 Jun 2026 12:20:44 +0000 Subject: [PATCH 1/3] Carry originalName into parameter/result mismatch errors --- .../Builtins.Http.Client/Libs/HttpClient.fs | 2 ++ backend/src/LibExecution/Interpreter.fs | 1 + backend/src/LibExecution/RuntimeTypes.fs | 18 ++++++++++++++++++ .../LibExecution/RuntimeTypesToDarkTypes.fs | 16 ++++++++++++++-- backend/src/LibExecution/TypeChecker.fs | 4 ++++ .../darklang/languageTools/runtimeErrors.dark | 4 ++-- .../darklang/prettyPrinter/runtimeError.dark | 18 ++++++++++++++---- 7 files changed, 55 insertions(+), 8 deletions(-) diff --git a/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs b/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs index 725dabff76..e435ba67ef 100644 --- a/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs +++ b/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs @@ -692,6 +692,7 @@ let fns (config : Configuration) : List = 2, "headers", VT.list (VT.tuple VT.string VT.string []), + [], Dval.toValueType notAPair, notAPair ) @@ -824,6 +825,7 @@ let fns (config : Configuration) : List = 2, "headers", VT.list (VT.tuple VT.string VT.string []), + [], Dval.toValueType notAPair, notAPair ) diff --git a/backend/src/LibExecution/Interpreter.fs b/backend/src/LibExecution/Interpreter.fs index e6eb30a2b1..2563c4a496 100644 --- a/backend/src/LibExecution/Interpreter.fs +++ b/backend/src/LibExecution/Interpreter.fs @@ -1134,6 +1134,7 @@ let rec private executeInner (exeState : ExecutionState) (vm : VMState) : Ply * actualType : ValueType * actualValue : Dval | FnResultNotExpectedType of fnName : FQFnName.FQFnName * expectedType : ValueType * + // See `expectedTypeName` on `FnParameterNotExpectedType`. + expectedTypeName : List * actualType : ValueType * actualValue : Dval @@ -2039,6 +2047,16 @@ module TypeReference = | _ -> Ply typ + /// The type's name as written at the reference site (the qualifiers), for a + /// custom type. Empty for built-in/primitive types, which carry no package + /// name. Used to report the as-written name in errors, since reducing to a + /// `ValueType` keeps only the hash (and thus the canonical sibling's name). + let originalNameOf (typeRef : TypeReference) : List = + match typeRef with + | TCustomType(nr, _) -> nr.originalName + | _ -> [] + + let rec toVT (types : Types) (tst : TypeSymbolTable) diff --git a/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs b/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs index ec1e844dac..518187a594 100644 --- a/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs +++ b/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs @@ -1223,6 +1223,7 @@ module RuntimeError = paramIndex, paramName, expectedType, + expectedTypeName, actualType, actualValue) -> "FnParameterNotExpectedType", @@ -1230,15 +1231,18 @@ module RuntimeError = DInt64 paramIndex DString paramName ValueType.toDT expectedType + DList(VT.string, List.map DString expectedTypeName) ValueType.toDT actualType Dval.toDT actualValue ] | RuntimeError.Applications.FnResultNotExpectedType(fnName, expectedType, + expectedTypeName, actualType, actualValue) -> "FnResultNotExpectedType", [ FQFnName.toDT fnName ValueType.toDT expectedType + DList(VT.string, List.map DString expectedTypeName) ValueType.toDT actualType Dval.toDT actualValue ] @@ -1280,12 +1284,19 @@ module RuntimeError = _, [], "FnParameterNotExpectedType", - [ fnName; paramIndex; paramName; expectedType; actualType; actualValue ]) -> + [ fnName + paramIndex + paramName + expectedType + expectedTypeName + actualType + actualValue ]) -> RuntimeError.Applications.FnParameterNotExpectedType( FQFnName.fromDT fnName, D.int64 paramIndex, D.string paramName, ValueType.fromDT expectedType, + D.list D.string expectedTypeName, ValueType.fromDT actualType, Dval.fromDT actualValue ) @@ -1293,10 +1304,11 @@ module RuntimeError = _, [], "FnResultNotExpectedType", - [ fnName; expectedType; actualType; actualValue ]) -> + [ fnName; expectedType; expectedTypeName; actualType; actualValue ]) -> RuntimeError.Applications.FnResultNotExpectedType( FQFnName.fromDT fnName, ValueType.fromDT expectedType, + D.list D.string expectedTypeName, ValueType.fromDT actualType, Dval.fromDT actualValue ) diff --git a/backend/src/LibExecution/TypeChecker.fs b/backend/src/LibExecution/TypeChecker.fs index 69d7652364..0325d0bac0 100644 --- a/backend/src/LibExecution/TypeChecker.fs +++ b/backend/src/LibExecution/TypeChecker.fs @@ -251,6 +251,7 @@ let checkFnParam (actual : Dval) : Ply> = uply { + let expectedName = TypeReference.originalNameOf expected let! expected = TypeReference.unwrapAlias types expected match! unify types tst expected actual with | Ok updatedTst -> return Ok updatedTst @@ -262,6 +263,7 @@ let checkFnParam paramIndex, paramName, expected, + expectedName, Dval.toValueType actual, actual ) @@ -278,6 +280,7 @@ let checkFnResult (actual : Dval) : Ply> = uply { + let expectedName = TypeReference.originalNameOf expected let! expected = TypeReference.unwrapAlias types expected let! expectedVT = TypeReference.toVT types tst expected match! unify types tst expected actual with @@ -287,6 +290,7 @@ let checkFnResult RTE.Applications.FnResultNotExpectedType( fnName, expectedVT, + expectedName, Dval.toValueType actual, actual ) diff --git a/packages/darklang/languageTools/runtimeErrors.dark b/packages/darklang/languageTools/runtimeErrors.dark index 0b04580f15..1398cd4b3e 100644 --- a/packages/darklang/languageTools/runtimeErrors.dark +++ b/packages/darklang/languageTools/runtimeErrors.dark @@ -110,8 +110,8 @@ module Applications = | WrongNumberOfTypeArgsForFn of fn: FQFnName.FQFnName * expected: Int64 * actual: Int64 | CannotApplyTypeArgsMoreThanOnce | TooManyArgsForFn of fn: FQFnName.FQFnName * expected: Int64 * actual: Int64 - | FnParameterNotExpectedType of fnName : FQFnName.FQFnName * paramIndex: Int64 * paramName : String * expectedType : ValueType * actualType : ValueType * actualValue : Dval - | FnResultNotExpectedType of fnName : FQFnName.FQFnName * expectedType : ValueType * actualType : ValueType * actualValue : Dval + | FnParameterNotExpectedType of fnName : FQFnName.FQFnName * paramIndex: Int64 * paramName : String * expectedType : ValueType * expectedTypeName : List * actualType : ValueType * actualValue : Dval + | FnResultNotExpectedType of fnName : FQFnName.FQFnName * expectedType : ValueType * expectedTypeName : List * actualType : ValueType * actualValue : Dval | CannotApplyTypeArgsToLambda | TooManyArgsForLambda of lambdaExprId: ID * expected: Int64 * actual: Int64 diff --git a/packages/darklang/prettyPrinter/runtimeError.dark b/packages/darklang/prettyPrinter/runtimeError.dark index a749411732..1d8da327fb 100644 --- a/packages/darklang/prettyPrinter/runtimeError.dark +++ b/packages/darklang/prettyPrinter/runtimeError.dark @@ -146,6 +146,16 @@ module RuntimeError = | _ -> "" + /// Renders the "expected type" in a mismatch. Prefers the name as written at + /// the declaration site (`expectedTypeName`) when present; otherwise falls + /// back to the value type, which for a custom type shows the canonical name + /// of any structurally-identical sibling rather than the name written. + let expectedSegment (expectedType: ValueType) (expectedTypeName: List) : ES = + match expectedTypeName with + | [] -> ES.ValueType expectedType + | names -> ES.String(Stdlib.String.join names ".") + + let toSegments (e: LanguageTools.RuntimeTypes.RuntimeError.Error) : ErrorSegments = @@ -431,23 +441,23 @@ module RuntimeError = ES.Count(expected, ES.String "argument", ES.String "arguments") ES.String ", but got " ES.Count(actual, ES.String "argument", ES.String "arguments") ] - | FnParameterNotExpectedType(fnName, paramIndex, paramName, expectedType, actualType, actualValue) -> + | FnParameterNotExpectedType(fnName, paramIndex, paramName, expectedType, expectedTypeName, actualType, actualValue) -> [ ES.FunctionName fnName ES.String "'s " ES.Ordinal(paramIndex + 1L) ES.String " parameter " ES.ParamName paramName ES.String " expects " - ES.ValueType expectedType + (expectedSegment expectedType expectedTypeName) ES.String ", but got " ES.ValueType actualType ES.String " (" ES.FullValue actualValue ES.String ")" ] - | FnResultNotExpectedType(fnName, expectedType, actualType, actualValue) -> + | FnResultNotExpectedType(fnName, expectedType, expectedTypeName, actualType, actualValue) -> [ ES.FunctionName fnName ES.String "'s return value expects " - ES.ValueType expectedType + (expectedSegment expectedType expectedTypeName) ES.String ", but got " ES.ValueType actualType ES.String " (" From e4867cd643f5394bb7500e968c0a8929517f7b36 Mon Sep 17 00:00:00 2001 From: OceanOak Date: Fri, 26 Jun 2026 12:59:52 +0000 Subject: [PATCH 2/3] Fix expected type names in mismatch errors --- .../Builtins.Http.Client/Libs/HttpClient.fs | 4 +- .../src/Builtins/Builtins.Pure/Libs/Float.fs | 3 +- .../src/Builtins/Builtins.Pure/Libs/Int128.fs | 3 +- .../Builtins/Builtins.Pure/Libs/UInt128.fs | 3 +- .../src/Builtins/Builtins.Pure/Libs/UInt64.fs | 3 +- .../src/Builtins/Builtins.Pure/Libs/Uuid.fs | 3 +- backend/src/LibExecution/Interpreter.fs | 2 +- .../ProgramTypesToRuntimeTypes.fs | 18 ++++--- backend/src/LibExecution/RuntimeTypes.fs | 43 ++++++++++----- .../LibExecution/RuntimeTypesToDarkTypes.fs | 52 +++++++++++++++---- backend/src/LibExecution/TypeChecker.fs | 8 +-- .../Binary/Serializers/RT/Common.fs | 25 ++++++++- .../darklang/languageTools/runtimeErrors.dark | 4 +- .../darklang/prettyPrinter/runtimeError.dark | 29 ++++++----- .../darklang/prettyPrinter/runtimeTypes.dark | 25 +++++++++ 15 files changed, 171 insertions(+), 54 deletions(-) diff --git a/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs b/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs index e435ba67ef..050589e4fd 100644 --- a/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs +++ b/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs @@ -692,7 +692,7 @@ let fns (config : Configuration) : List = 2, "headers", VT.list (VT.tuple VT.string VT.string []), - [], + None, Dval.toValueType notAPair, notAPair ) @@ -825,7 +825,7 @@ let fns (config : Configuration) : List = 2, "headers", VT.list (VT.tuple VT.string VT.string []), - [], + None, Dval.toValueType notAPair, notAPair ) diff --git a/backend/src/Builtins/Builtins.Pure/Libs/Float.fs b/backend/src/Builtins/Builtins.Pure/Libs/Float.fs index 143d271dd0..9208ccb60f 100644 --- a/backend/src/Builtins/Builtins.Pure/Libs/Float.fs +++ b/backend/src/Builtins/Builtins.Pure/Libs/Float.fs @@ -301,7 +301,8 @@ let fns () : List = (TCustomType( { originalName = [] resolved = - Ok(FQTypeName.fqPackage (PackageRefs.Type.Stdlib.floatParseError ())) }, + Ok(FQTypeName.fqPackage (PackageRefs.Type.Stdlib.floatParseError ())) + location = None }, [] )) description = diff --git a/backend/src/Builtins/Builtins.Pure/Libs/Int128.fs b/backend/src/Builtins/Builtins.Pure/Libs/Int128.fs index e3950a9b6c..d6430c58e2 100644 --- a/backend/src/Builtins/Builtins.Pure/Libs/Int128.fs +++ b/backend/src/Builtins/Builtins.Pure/Libs/Int128.fs @@ -315,7 +315,8 @@ let fns () : List = resolved = Ok( FQTypeName.fqPackage (PackageRefs.Type.Stdlib.int128ParseError ()) - ) }, + ) + location = None }, [] )) description = "Returns the value of a " diff --git a/backend/src/Builtins/Builtins.Pure/Libs/UInt128.fs b/backend/src/Builtins/Builtins.Pure/Libs/UInt128.fs index f68202d17c..9377eec2ec 100644 --- a/backend/src/Builtins/Builtins.Pure/Libs/UInt128.fs +++ b/backend/src/Builtins/Builtins.Pure/Libs/UInt128.fs @@ -259,7 +259,8 @@ let fns () : List = resolved = Ok( FQTypeName.fqPackage (PackageRefs.Type.Stdlib.uint128ParseError ()) - ) }, + ) + location = None }, [] )) description = "Returns the value of a " diff --git a/backend/src/Builtins/Builtins.Pure/Libs/UInt64.fs b/backend/src/Builtins/Builtins.Pure/Libs/UInt64.fs index 8b228ce289..488363d8e4 100644 --- a/backend/src/Builtins/Builtins.Pure/Libs/UInt64.fs +++ b/backend/src/Builtins/Builtins.Pure/Libs/UInt64.fs @@ -278,7 +278,8 @@ let fns () : List = resolved = Ok( FQTypeName.fqPackage (PackageRefs.Type.Stdlib.uint64ParseError ()) - ) }, + ) + location = None }, [] )) description = "Returns the value of a " diff --git a/backend/src/Builtins/Builtins.Pure/Libs/Uuid.fs b/backend/src/Builtins/Builtins.Pure/Libs/Uuid.fs index b6709833e6..e7f66dc0f6 100644 --- a/backend/src/Builtins/Builtins.Pure/Libs/Uuid.fs +++ b/backend/src/Builtins/Builtins.Pure/Libs/Uuid.fs @@ -30,7 +30,8 @@ let fns () : List = (TCustomType( { originalName = [] resolved = - Ok(FQTypeName.fqPackage (PackageRefs.Type.Stdlib.uuidParseError ())) }, + Ok(FQTypeName.fqPackage (PackageRefs.Type.Stdlib.uuidParseError ())) + location = None }, [] )) description = diff --git a/backend/src/LibExecution/Interpreter.fs b/backend/src/LibExecution/Interpreter.fs index 2563c4a496..af2d0c97e4 100644 --- a/backend/src/LibExecution/Interpreter.fs +++ b/backend/src/LibExecution/Interpreter.fs @@ -1134,7 +1134,7 @@ let rec private executeInner (exeState : ExecutionState) (vm : VMState) : Ply RT.NameResolutionError.NotFound | PT.NameResolutionError.InvalidName -> RT.NameResolutionError.InvalidName +module PackageLocation = + let toRT (l : PT.PackageLocation) : RT.PackageLocation = + { owner = l.owner; modules = l.modules; name = l.name } + module NameResolution = - // `location` is package-store metadata — load-bearing for propagation - // rewrites (AstTransformer's byLocation substitution), SCC hash - // substitution (Canonical writer), dep-edge inserts, and deferred - // refresh. The runtime executes against already-resolved hashes and - // has no use for any of that, so we drop location at the PT→RT boundary. + // PT.NameResolution.location is package-store metadata used by package + // rewriting, hashing, dependency tracking, and deferred refresh. RT keeps it + // only so runtime errors can render the package name that was referenced. let toRT (f : 'a -> 'b) (nr : PT.NameResolution<'a>) : RT.NameResolution<'b> = { originalName = nr.originalName resolved = match nr.resolved with | Ok resolved -> Ok(f resolved.name) - | Error e -> Error(NameResolutionError.toRT e) } + | Error e -> Error(NameResolutionError.toRT e) + location = + match nr.resolved with + | Ok resolved -> resolved.location |> Option.map PackageLocation.toRT + | Error _ -> None } module TypeReference = diff --git a/backend/src/LibExecution/RuntimeTypes.fs b/backend/src/LibExecution/RuntimeTypes.fs index 5304da9032..f66246f6c4 100644 --- a/backend/src/LibExecution/RuntimeTypes.fs +++ b/backend/src/LibExecution/RuntimeTypes.fs @@ -116,12 +116,24 @@ type NameResolutionError = | NotFound | InvalidName +/// The package location a reference resolved to (owner.modules.name). Carried +/// from PT so error messages can name the referenced item when multiple names +/// share the same hash. +type PackageLocation = { owner : string; modules : List; name : string } + type NameResolution<'a> = - { originalName : List; resolved : Result<'a, NameResolutionError> } + { + originalName : List + resolved : Result<'a, NameResolutionError> + /// The resolved item's location, when known (only meaningful when + /// `resolved` is `Ok`). Preserved from PT to disambiguate the rendered + /// name of structurally-identical siblings. + location : Option + } module NameResolution = let ok (value : 'a) : NameResolution<'a> = - { originalName = []; resolved = Ok value } + { originalName = []; resolved = Ok value; location = None } /// A KnownType represents the type of a dval. @@ -997,20 +1009,18 @@ module RuntimeError = paramIndex : int64 * paramName : string * expectedType : ValueType * - // The expected type's name as written at the parameter's declaration - // site, when it is a custom type ([] otherwise). Carried separately - // because `expectedType` is reduced to a hash, which renders as the - // canonical name of any structurally-identical sibling rather than the - // name actually written. - expectedTypeName : List * + // The package location the expected type resolved to, when it's a + // custom type (None otherwise). Used by error rendering to name the + // referenced type when multiple names share the same hash. + expectedTypeLocation : Option * actualType : ValueType * actualValue : Dval | FnResultNotExpectedType of fnName : FQFnName.FQFnName * expectedType : ValueType * - // See `expectedTypeName` on `FnParameterNotExpectedType`. - expectedTypeName : List * + // See `expectedTypeLocation` on `FnParameterNotExpectedType`. + expectedTypeLocation : Option * actualType : ValueType * actualValue : Dval @@ -2023,14 +2033,16 @@ module TypeReference = let result (t1 : TypeReference) (t2 : TypeReference) : TypeReference = TCustomType( { originalName = [] - resolved = Ok(FQTypeName.fqPackage (PackageRefs.Type.Stdlib.result ())) }, + resolved = Ok(FQTypeName.fqPackage (PackageRefs.Type.Stdlib.result ())) + location = None }, [ t1; t2 ] ) let option (t : TypeReference) : TypeReference = TCustomType( { originalName = [] - resolved = Ok(FQTypeName.fqPackage (PackageRefs.Type.Stdlib.option ())) }, + resolved = Ok(FQTypeName.fqPackage (PackageRefs.Type.Stdlib.option ())) + location = None }, [ t ] ) @@ -2056,6 +2068,13 @@ module TypeReference = | TCustomType(nr, _) -> nr.originalName | _ -> [] + /// The resolved package location of a custom type reference, when known. + /// Used by runtime error rendering. + let locationOf (typeRef : TypeReference) : Option = + match typeRef with + | TCustomType(nr, _) -> nr.location + | _ -> None + let rec toVT (types : Types) diff --git a/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs b/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs index 518187a594..6cb5235902 100644 --- a/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs +++ b/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs @@ -173,6 +173,32 @@ module NameResolutionError = | _ -> Exception.raiseInternal "Invalid NameResolutionError" [] +module PackageLocation = + // Runtime PackageLocation is encoded as ProgramTypes.PackageLocation because + // the pretty-printer already expects that public Dark type. + let typeName () = + FQTypeName.fqPackage ( + PackageRefs.Type.LanguageTools.ProgramTypes.packageLocation () + ) + + let knownType () = KTCustomType(typeName (), []) + + let toDT (loc : PackageLocation) : Dval = + let fields = + [ "owner", DString loc.owner + "modules", DList(VT.string, List.map DString loc.modules) + "name", DString loc.name ] + DRecord(typeName (), typeName (), [], Map fields) + + let fromDT (d : Dval) : PackageLocation = + match d with + | DRecord(_, _, _, fields) -> + { owner = C2DT.ownerField fields + modules = C2DT.modulesField fields + name = C2DT.nameField fields } + | _ -> Exception.raiseInternal "Invalid PackageLocation" [] + + module NameResolution = let typeName () = FQTypeName.fqPackage ( @@ -208,7 +234,9 @@ module NameResolution = f (fields |> D.field "resolved") NameResolutionError.fromDT - { originalName = originalName; resolved = resolved } + // NameResolution.location is not part of the RuntimeTypes.NameResolution + // DT representation; default it to None when decoding that type. + { originalName = originalName; resolved = resolved; location = None } | _ -> Exception.raiseInternal "Invalid NameResolution" [] @@ -1223,7 +1251,7 @@ module RuntimeError = paramIndex, paramName, expectedType, - expectedTypeName, + expectedTypeLocation, actualType, actualValue) -> "FnParameterNotExpectedType", @@ -1231,18 +1259,24 @@ module RuntimeError = DInt64 paramIndex DString paramName ValueType.toDT expectedType - DList(VT.string, List.map DString expectedTypeName) + C2DT.Option.toDT + PackageLocation.toDT + (PackageLocation.knownType ()) + expectedTypeLocation ValueType.toDT actualType Dval.toDT actualValue ] | RuntimeError.Applications.FnResultNotExpectedType(fnName, expectedType, - expectedTypeName, + expectedTypeLocation, actualType, actualValue) -> "FnResultNotExpectedType", [ FQFnName.toDT fnName ValueType.toDT expectedType - DList(VT.string, List.map DString expectedTypeName) + C2DT.Option.toDT + PackageLocation.toDT + (PackageLocation.knownType ()) + expectedTypeLocation ValueType.toDT actualType Dval.toDT actualValue ] @@ -1288,7 +1322,7 @@ module RuntimeError = paramIndex paramName expectedType - expectedTypeName + expectedTypeLocation actualType actualValue ]) -> RuntimeError.Applications.FnParameterNotExpectedType( @@ -1296,7 +1330,7 @@ module RuntimeError = D.int64 paramIndex, D.string paramName, ValueType.fromDT expectedType, - D.list D.string expectedTypeName, + C2DT.Option.fromDT PackageLocation.fromDT expectedTypeLocation, ValueType.fromDT actualType, Dval.fromDT actualValue ) @@ -1304,11 +1338,11 @@ module RuntimeError = _, [], "FnResultNotExpectedType", - [ fnName; expectedType; expectedTypeName; actualType; actualValue ]) -> + [ fnName; expectedType; expectedTypeLocation; actualType; actualValue ]) -> RuntimeError.Applications.FnResultNotExpectedType( FQFnName.fromDT fnName, ValueType.fromDT expectedType, - D.list D.string expectedTypeName, + C2DT.Option.fromDT PackageLocation.fromDT expectedTypeLocation, ValueType.fromDT actualType, Dval.fromDT actualValue ) diff --git a/backend/src/LibExecution/TypeChecker.fs b/backend/src/LibExecution/TypeChecker.fs index 0325d0bac0..4edbf49fe9 100644 --- a/backend/src/LibExecution/TypeChecker.fs +++ b/backend/src/LibExecution/TypeChecker.fs @@ -251,7 +251,7 @@ let checkFnParam (actual : Dval) : Ply> = uply { - let expectedName = TypeReference.originalNameOf expected + let expectedLocation = TypeReference.locationOf expected let! expected = TypeReference.unwrapAlias types expected match! unify types tst expected actual with | Ok updatedTst -> return Ok updatedTst @@ -263,7 +263,7 @@ let checkFnParam paramIndex, paramName, expected, - expectedName, + expectedLocation, Dval.toValueType actual, actual ) @@ -280,7 +280,7 @@ let checkFnResult (actual : Dval) : Ply> = uply { - let expectedName = TypeReference.originalNameOf expected + let expectedLocation = TypeReference.locationOf expected let! expected = TypeReference.unwrapAlias types expected let! expectedVT = TypeReference.toVT types tst expected match! unify types tst expected actual with @@ -290,7 +290,7 @@ let checkFnResult RTE.Applications.FnResultNotExpectedType( fnName, expectedVT, - expectedName, + expectedLocation, Dval.toValueType actual, actual ) diff --git a/backend/src/LibSerialization/Binary/Serializers/RT/Common.fs b/backend/src/LibSerialization/Binary/Serializers/RT/Common.fs index e6c77e7ce0..2302b5c82c 100644 --- a/backend/src/LibSerialization/Binary/Serializers/RT/Common.fs +++ b/backend/src/LibSerialization/Binary/Serializers/RT/Common.fs @@ -26,6 +26,19 @@ module NameResolutionError = | b -> raiseFormatError $"Invalid NameResolutionError tag: {b}" +module PackageLocation = + let write (w : BinaryWriter) (loc : PackageLocation) : unit = + String.write w loc.owner + List.write w String.write loc.modules + String.write w loc.name + + let read (r : BinaryReader) : PackageLocation = + let owner = String.read r + let modules = List.read r String.read + let name = String.read r + { owner = owner; modules = modules; name = name } + + module NameResolution = let write (writeInner : BinaryWriter -> 'inner -> unit) @@ -40,6 +53,11 @@ module NameResolution = | Error error -> w.Write 1uy NameResolutionError.write w error + match nr.location with + | None -> w.Write 0uy + | Some loc -> + w.Write 1uy + PackageLocation.write w loc let read (readInner : BinaryReader -> 'inner) @@ -53,7 +71,12 @@ module NameResolution = let error = NameResolutionError.read r Error(error) | b -> raiseFormatError $"Invalid NameResolution tag: {b}" - { originalName = originalName; resolved = resolved } + let location = + match r.ReadByte() with + | 0uy -> None + | 1uy -> Some(PackageLocation.read r) + | b -> raiseFormatError $"Invalid NameResolution location tag: {b}" + { originalName = originalName; resolved = resolved; location = location } module FQTypeName = diff --git a/packages/darklang/languageTools/runtimeErrors.dark b/packages/darklang/languageTools/runtimeErrors.dark index 1398cd4b3e..e24af100da 100644 --- a/packages/darklang/languageTools/runtimeErrors.dark +++ b/packages/darklang/languageTools/runtimeErrors.dark @@ -110,8 +110,8 @@ module Applications = | WrongNumberOfTypeArgsForFn of fn: FQFnName.FQFnName * expected: Int64 * actual: Int64 | CannotApplyTypeArgsMoreThanOnce | TooManyArgsForFn of fn: FQFnName.FQFnName * expected: Int64 * actual: Int64 - | FnParameterNotExpectedType of fnName : FQFnName.FQFnName * paramIndex: Int64 * paramName : String * expectedType : ValueType * expectedTypeName : List * actualType : ValueType * actualValue : Dval - | FnResultNotExpectedType of fnName : FQFnName.FQFnName * expectedType : ValueType * expectedTypeName : List * actualType : ValueType * actualValue : Dval + | FnParameterNotExpectedType of fnName : FQFnName.FQFnName * paramIndex: Int64 * paramName : String * expectedType : ValueType * expectedTypeLocation : Stdlib.Option.Option * actualType : ValueType * actualValue : Dval + | FnResultNotExpectedType of fnName : FQFnName.FQFnName * expectedType : ValueType * expectedTypeLocation : Stdlib.Option.Option * actualType : ValueType * actualValue : Dval | CannotApplyTypeArgsToLambda | TooManyArgsForLambda of lambdaExprId: ID * expected: Int64 * actual: Int64 diff --git a/packages/darklang/prettyPrinter/runtimeError.dark b/packages/darklang/prettyPrinter/runtimeError.dark index 1d8da327fb..f2b55521bf 100644 --- a/packages/darklang/prettyPrinter/runtimeError.dark +++ b/packages/darklang/prettyPrinter/runtimeError.dark @@ -38,6 +38,9 @@ module RuntimeError = | TypeReference of TypeReference | TypeOfValue of Dval // CLEANUP should these all just be ValueTypes? | ValueType of ValueType + /// Renders custom expected types using the package name that was actually + /// referenced. `None` falls back to normal ValueType rendering. + | ExpectedType of ValueType * Stdlib.Option.Option | FieldName of String // records and enums | InlineFieldName of String // records and enums @@ -102,6 +105,7 @@ module RuntimeError = | TypeReference t -> typeReference branchId t | TypeOfValue dv -> Dval.valueTypeName branchId dv | ValueType vt -> valueType branchId vt + | ExpectedType(vt, location) -> valueTypeAtLocation branchId location vt | FieldName f -> $"`{f}`" | InlineFieldName f -> f | DBName db -> db @@ -146,14 +150,15 @@ module RuntimeError = | _ -> "" - /// Renders the "expected type" in a mismatch. Prefers the name as written at - /// the declaration site (`expectedTypeName`) when present; otherwise falls - /// back to the value type, which for a custom type shows the canonical name - /// of any structurally-identical sibling rather than the name written. - let expectedSegment (expectedType: ValueType) (expectedTypeName: List) : ES = - match expectedTypeName with - | [] -> ES.ValueType expectedType - | names -> ES.String(Stdlib.String.join names ".") + /// Renders the "expected type" in a mismatch. Renders the value type (keeping + /// type arguments), but names the outermost custom type from its resolved + /// `location` so a structurally-identical sibling's canonical name isn't + /// shown in its place (e.g. the byte-identical `Int*.ParseError` enums). + let expectedSegment + (expectedType: ValueType) + (expectedTypeLocation: Stdlib.Option.Option) + : ES = + ES.ExpectedType(expectedType, expectedTypeLocation) let toSegments @@ -441,23 +446,23 @@ module RuntimeError = ES.Count(expected, ES.String "argument", ES.String "arguments") ES.String ", but got " ES.Count(actual, ES.String "argument", ES.String "arguments") ] - | FnParameterNotExpectedType(fnName, paramIndex, paramName, expectedType, expectedTypeName, actualType, actualValue) -> + | FnParameterNotExpectedType(fnName, paramIndex, paramName, expectedType, expectedTypeLocation, actualType, actualValue) -> [ ES.FunctionName fnName ES.String "'s " ES.Ordinal(paramIndex + 1L) ES.String " parameter " ES.ParamName paramName ES.String " expects " - (expectedSegment expectedType expectedTypeName) + (expectedSegment expectedType expectedTypeLocation) ES.String ", but got " ES.ValueType actualType ES.String " (" ES.FullValue actualValue ES.String ")" ] - | FnResultNotExpectedType(fnName, expectedType, expectedTypeName, actualType, actualValue) -> + | FnResultNotExpectedType(fnName, expectedType, expectedTypeLocation, actualType, actualValue) -> [ ES.FunctionName fnName ES.String "'s return value expects " - (expectedSegment expectedType expectedTypeName) + (expectedSegment expectedType expectedTypeLocation) ES.String ", but got " ES.ValueType actualType ES.String " (" diff --git a/packages/darklang/prettyPrinter/runtimeTypes.dark b/packages/darklang/prettyPrinter/runtimeTypes.dark index d196ba8958..79c50d7a19 100644 --- a/packages/darklang/prettyPrinter/runtimeTypes.dark +++ b/packages/darklang/prettyPrinter/runtimeTypes.dark @@ -278,6 +278,31 @@ let valueType | Known kt -> knownType branchId kt | Unknown -> "_" +/// Like `valueType`, but when the *outermost* type is a custom type and a +/// resolved `location` is known, renders the name from that exact location +/// (the referenced sibling) rather than reverse-resolving the hash to an +/// arbitrary canonical sibling. Type arguments and nested types render +/// normally. With no location, or for non-custom types, this is `valueType`. +let valueTypeAtLocation + (branchId: Uuid) + (location: Stdlib.Option.Option) + (vt: LanguageTools.RuntimeTypes.ValueType) + : String = + match location, vt with + | Some loc, Known(KTCustomType(_name, typeArgs)) -> + let typeArgsPortion = + match typeArgs with + | [] -> "" + | args -> + args + |> Stdlib.List.map (fun t -> valueType branchId t) + |> Stdlib.String.join ", " + |> fun betweenBrackets -> "<" ++ betweenBrackets ++ ">" + + (packageName loc.owner loc.modules loc.name) ++ typeArgsPortion + + | _ -> valueType branchId vt + let letPattern (pat: LanguageTools.RuntimeTypes.LetPattern) : String = match pat with | LPVariable _reg -> From e7b9aba0dc984c8ce5d0f74c48802cf88792b8bf Mon Sep 17 00:00:00 2001 From: OceanOak Date: Fri, 26 Jun 2026 13:49:53 +0000 Subject: [PATCH 3/3] Fix function names in mismatch errors --- .../Builtins.Http.Client/Libs/HttpClient.fs | 2 ++ .../Builtins/Builtins.Pure/Libs/NoModule.fs | 9 +++++- backend/src/LibExecution/Execution.fs | 1 + backend/src/LibExecution/Interpreter.fs | 13 ++++++-- .../ProgramTypesToRuntimeTypes.fs | 3 ++ backend/src/LibExecution/RTQueryCompiler.fs | 1 + backend/src/LibExecution/RuntimeTypes.fs | 27 ++++++++++++++-- .../LibExecution/RuntimeTypesToDarkTypes.fs | 21 ++++++++++++- backend/src/LibExecution/TypeChecker.fs | 4 +++ .../Binary/Serializers/RT/Dval.fs | 11 +++++++ backend/tests/Tests/Blob.Tests.fs | 1 + backend/tests/Tests/Interpreter.Tests.fs | 5 +++ backend/tests/Tests/PT2RT.Tests.fs | 31 +++++++++++++++++++ .../darklang/languageTools/runtimeErrors.dark | 4 +-- .../darklang/prettyPrinter/runtimeError.dark | 25 ++++++++++++--- 15 files changed, 145 insertions(+), 13 deletions(-) diff --git a/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs b/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs index 050589e4fd..5a555fd397 100644 --- a/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs +++ b/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs @@ -689,6 +689,7 @@ let fns (config : Configuration) : List = FQFnName.fqPackage ( PackageRefs.Fn.Stdlib.HttpClient.request () ), + None, 2, "headers", VT.list (VT.tuple VT.string VT.string []), @@ -822,6 +823,7 @@ let fns (config : Configuration) : List = FQFnName.fqPackage ( PackageRefs.Fn.Stdlib.HttpClient.stream () ), + None, 2, "headers", VT.list (VT.tuple VT.string VT.string []), diff --git a/backend/src/Builtins/Builtins.Pure/Libs/NoModule.fs b/backend/src/Builtins/Builtins.Pure/Libs/NoModule.fs index 3edddcc99b..6150241942 100644 --- a/backend/src/Builtins/Builtins.Pure/Libs/NoModule.fs +++ b/backend/src/Builtins/Builtins.Pure/Libs/NoModule.fs @@ -110,7 +110,14 @@ let rec equals (a : Dval) (b : Dval) : bool = // equality needs lambda-internal-state work. Today this is // "same-source-position lambdas compare equal." | AppLambda a, AppLambda b -> a.exprId = b.exprId - | AppNamedFn a, AppNamedFn b -> a = b + | AppNamedFn a, AppNamedFn b -> + // `location` is excluded: it records how the function was named at the + // reference site (for error messages), not its identity. Two references + // to the same function by different names are the same function. + a.name = b.name + && a.typeArgs = b.typeArgs + && a.argsSoFar = b.argsSoFar + && a.typeSymbolTable = b.typeSymbolTable | _ -> false | DDB a, DDB b -> a = b diff --git a/backend/src/LibExecution/Execution.fs b/backend/src/LibExecution/Execution.fs index cdd87e7181..686a42405c 100644 --- a/backend/src/LibExecution/Execution.fs +++ b/backend/src/LibExecution/Execution.fs @@ -191,6 +191,7 @@ let executeFunction let fnInstr, fnReg, rc = let namedFn : RT.ApplicableNamedFn = { name = name + location = None typeSymbolTable = Map.empty typeArgs = typeArgs argsSoFar = [] } diff --git a/backend/src/LibExecution/Interpreter.fs b/backend/src/LibExecution/Interpreter.fs index af2d0c97e4..0f49553a7d 100644 --- a/backend/src/LibExecution/Interpreter.fs +++ b/backend/src/LibExecution/Interpreter.fs @@ -680,7 +680,8 @@ let rec private executeInner (exeState : ExecutionState) (vm : VMState) : Ply raiseRTE let typeCheckParam = - TypeChecker.checkFnParam exeState.types applicable.name + TypeChecker.checkFnParam + exeState.types + applicable.name + applicable.location let mutable tst = if Map.isEmpty applicable.typeSymbolTable then @@ -890,6 +894,7 @@ let rec private executeInner (exeState : ExecutionState) (vm : VMState) : Ply List.iteri (fun i arg -> r[i] <- arg) r typeSymbolTable = frameTst - executionPoint = pkgEp } + executionPoint = pkgEp + fnLocation = applicable.location } |> Some | RaiseNRE(names, nre) -> raiseRTE (RTE.ParseTimeNameResolution(names, nre)) @@ -1133,6 +1139,7 @@ let rec private executeInner (exeState : ExecutionState) (vm : VMState) : Ply RT.FQFnName.Builtin + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -919,6 +921,7 @@ module Expr = | PT.EFnName(_, { resolved = Ok resolved }) -> let namedFn : RT.ApplicableNamedFn = { name = FQFnName.toRT resolved.name + location = resolved.location |> Option.map PackageLocation.toRT typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } diff --git a/backend/src/LibExecution/RTQueryCompiler.fs b/backend/src/LibExecution/RTQueryCompiler.fs index 4efe50dee7..53bbe055ab 100644 --- a/backend/src/LibExecution/RTQueryCompiler.fs +++ b/backend/src/LibExecution/RTQueryCompiler.fs @@ -100,6 +100,7 @@ let partialEvaluate // Load the function reference let appFn : RT.ApplicableNamedFn = { name = fnName + location = None typeSymbolTable = Map.empty typeArgs = typeArgs argsSoFar = [] } diff --git a/backend/src/LibExecution/RuntimeTypes.fs b/backend/src/LibExecution/RuntimeTypes.fs index f66246f6c4..871b0d79f7 100644 --- a/backend/src/LibExecution/RuntimeTypes.fs +++ b/backend/src/LibExecution/RuntimeTypes.fs @@ -591,14 +591,23 @@ and LambdaImpl = and ApplicableNamedFn = - { name : FQFnName.FQFnName + { + name : FQFnName.FQFnName + + /// The package location the function reference resolved to, when known + /// (None for built-ins / re-derived applicables). Carried so error messages + /// name the *referenced* function rather than the canonical name of a + /// structurally-identical sibling that shares its hash. Excluded from + /// equality (see `Builtins.Pure.Libs.NoModule`). + location : Option typeSymbolTable : TypeSymbolTable // CLEANUP maybe this could be List? typeArgs : List - argsSoFar : List } + argsSoFar : List + } and ApplicableLambda = { @@ -1006,6 +1015,10 @@ module RuntimeError = | FnParameterNotExpectedType of fnName : FQFnName.FQFnName * + // The package location the called function resolved to, when known. + // Like `expectedTypeLocation`, lets the message name the referenced + // function rather than a canonical sibling that shares its hash. + fnNameLocation : Option * paramIndex : int64 * paramName : string * expectedType : ValueType * @@ -1018,6 +1031,8 @@ module RuntimeError = | FnResultNotExpectedType of fnName : FQFnName.FQFnName * + // See `fnNameLocation` on `FnParameterNotExpectedType`. + fnNameLocation : Option * expectedType : ValueType * // See `expectedTypeLocation` on `FnParameterNotExpectedType`. expectedTypeLocation : Option * @@ -1662,6 +1677,13 @@ type CallFrame = // so we keep only one copy of such, in the root of the VMState executionPoint : ExecutionPoint + /// The package location the called function was referenced by, when known. + /// `executionPoint` keeps only the hash, which renders as the canonical name + /// of a structurally-identical sibling; this lets a result-type-mismatch + /// error name the function as it was actually called. None for the root + /// frame, lambdas, and built-ins. + fnLocation : Option + /// What instruction index we are currently 'at' mutable programCounter : int @@ -1777,6 +1799,7 @@ type VMState = let rootCallFrame : CallFrame = { id = rootCallFrameID executionPoint = Source + fnLocation = None programCounter = 0 registers = Array.zeroCreate instrs.registerCount typeSymbolTable = Map.empty diff --git a/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs b/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs index 6cb5235902..ea5611932f 100644 --- a/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs +++ b/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs @@ -600,6 +600,7 @@ module ApplicableNamedFn = match d with | DRecord(_, _, _, fields) -> { name = FQFnName.fromDT (fields |> D.field "name") + location = None typeSymbolTable = Map.empty // TODO typeArgs = fields |> D.field "typeArgs" |> D.list TypeReference.fromDT argsSoFar = fields |> D.field "argsSoFar" |> D.list Dval.fromDT } @@ -1248,6 +1249,7 @@ module RuntimeError = | RuntimeError.Applications.TooManyArgsForFn(fn, expected, actual) -> "TooManyArgsForFn", [ FQFnName.toDT fn; DInt64 expected; DInt64 actual ] | RuntimeError.Applications.FnParameterNotExpectedType(fnName, + fnNameLocation, paramIndex, paramName, expectedType, @@ -1256,6 +1258,10 @@ module RuntimeError = actualValue) -> "FnParameterNotExpectedType", [ FQFnName.toDT fnName + C2DT.Option.toDT + PackageLocation.toDT + (PackageLocation.knownType ()) + fnNameLocation DInt64 paramIndex DString paramName ValueType.toDT expectedType @@ -1266,12 +1272,17 @@ module RuntimeError = ValueType.toDT actualType Dval.toDT actualValue ] | RuntimeError.Applications.FnResultNotExpectedType(fnName, + fnNameLocation, expectedType, expectedTypeLocation, actualType, actualValue) -> "FnResultNotExpectedType", [ FQFnName.toDT fnName + C2DT.Option.toDT + PackageLocation.toDT + (PackageLocation.knownType ()) + fnNameLocation ValueType.toDT expectedType C2DT.Option.toDT PackageLocation.toDT @@ -1319,6 +1330,7 @@ module RuntimeError = [], "FnParameterNotExpectedType", [ fnName + fnNameLocation paramIndex paramName expectedType @@ -1327,6 +1339,7 @@ module RuntimeError = actualValue ]) -> RuntimeError.Applications.FnParameterNotExpectedType( FQFnName.fromDT fnName, + C2DT.Option.fromDT PackageLocation.fromDT fnNameLocation, D.int64 paramIndex, D.string paramName, ValueType.fromDT expectedType, @@ -1338,9 +1351,15 @@ module RuntimeError = _, [], "FnResultNotExpectedType", - [ fnName; expectedType; expectedTypeLocation; actualType; actualValue ]) -> + [ fnName + fnNameLocation + expectedType + expectedTypeLocation + actualType + actualValue ]) -> RuntimeError.Applications.FnResultNotExpectedType( FQFnName.fromDT fnName, + C2DT.Option.fromDT PackageLocation.fromDT fnNameLocation, ValueType.fromDT expectedType, C2DT.Option.fromDT PackageLocation.fromDT expectedTypeLocation, ValueType.fromDT actualType, diff --git a/backend/src/LibExecution/TypeChecker.fs b/backend/src/LibExecution/TypeChecker.fs index 4edbf49fe9..371f6583aa 100644 --- a/backend/src/LibExecution/TypeChecker.fs +++ b/backend/src/LibExecution/TypeChecker.fs @@ -244,6 +244,7 @@ let rec resolveType let checkFnParam (types : Types) (fnName : FQFnName.FQFnName) + (fnNameLocation : Option) (tst : TypeSymbolTable) (paramIndex : int) (paramName : string) @@ -260,6 +261,7 @@ let checkFnParam return RTE.Applications.FnParameterNotExpectedType( fnName, + fnNameLocation, paramIndex, paramName, expected, @@ -275,6 +277,7 @@ let checkFnParam let checkFnResult (types : Types) (fnName : FQFnName.FQFnName) + (fnNameLocation : Option) (tst : TypeSymbolTable) (expected : TypeReference) (actual : Dval) @@ -289,6 +292,7 @@ let checkFnResult return RTE.Applications.FnResultNotExpectedType( fnName, + fnNameLocation, expectedVT, expectedLocation, Dval.toValueType actual, diff --git a/backend/src/LibSerialization/Binary/Serializers/RT/Dval.fs b/backend/src/LibSerialization/Binary/Serializers/RT/Dval.fs index f51744bd77..657f792a01 100644 --- a/backend/src/LibSerialization/Binary/Serializers/RT/Dval.fs +++ b/backend/src/LibSerialization/Binary/Serializers/RT/Dval.fs @@ -95,6 +95,11 @@ and writeApplicableLambda (w : BinaryWriter) (lambda : ApplicableLambda) = and writeApplicableNamedFn (w : BinaryWriter) (namedFn : ApplicableNamedFn) = FQFnName.write w namedFn.name + match namedFn.location with + | None -> w.Write 0uy + | Some loc -> + w.Write 1uy + PackageLocation.write w loc writeTypeSymbolTable w namedFn.typeSymbolTable List.write w TypeReference.write namedFn.typeArgs List.write w writeDval namedFn.argsSoFar @@ -272,10 +277,16 @@ and readApplicableLambda (r : BinaryReader) : ApplicableLambda = and readApplicableNamedFn (r : BinaryReader) : ApplicableNamedFn = let name = FQFnName.read r + let location = + match r.ReadByte() with + | 0uy -> None + | 1uy -> Some(PackageLocation.read r) + | b -> raiseFormatError $"Invalid ApplicableNamedFn location tag: {b}" let typeSymbolTable = readTypeSymbolTable r let typeArgs = List.read r TypeReference.read let argsSoFar = List.read r readDval { name = name + location = location typeSymbolTable = typeSymbolTable typeArgs = typeArgs argsSoFar = argsSoFar } diff --git a/backend/tests/Tests/Blob.Tests.fs b/backend/tests/Tests/Blob.Tests.fs index aa3d85cfae..68944859fd 100644 --- a/backend/tests/Tests/Blob.Tests.fs +++ b/backend/tests/Tests/Blob.Tests.fs @@ -683,6 +683,7 @@ let private fakeAppNamedFn (argsSoFar : List) : RT.Dval = RT.DApplicable( RT.AppNamedFn { name = name + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = argsSoFar } diff --git a/backend/tests/Tests/Interpreter.Tests.fs b/backend/tests/Tests/Interpreter.Tests.fs index 0c728eebe2..16df4083b4 100644 --- a/backend/tests/Tests/Interpreter.Tests.fs +++ b/backend/tests/Tests/Interpreter.Tests.fs @@ -589,6 +589,7 @@ module Fns = (RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -601,6 +602,7 @@ module Fns = (RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [ RT.DInt64 1 ] } @@ -628,6 +630,7 @@ module Fns = (RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqPackage E.Fns.Package.MyAdd.hash + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -640,6 +643,7 @@ module Fns = (RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqPackage E.Fns.Package.MyAdd.hash + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [ RT.DInt64 1 ] } @@ -660,6 +664,7 @@ module Fns = (RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqPackage E.Fns.Package.Fact.hash + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } diff --git a/backend/tests/Tests/PT2RT.Tests.fs b/backend/tests/Tests/PT2RT.Tests.fs index bbf87fbf8d..1b50c46cd3 100644 --- a/backend/tests/Tests/PT2RT.Tests.fs +++ b/backend/tests/Tests/PT2RT.Tests.fs @@ -365,6 +365,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Mod" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -377,6 +378,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "equals" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -517,6 +519,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "equals" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -626,6 +629,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -646,6 +650,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -674,6 +679,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -706,6 +712,7 @@ module Expr = (RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -731,6 +738,7 @@ module Expr = (RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "multiply" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -747,6 +755,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -760,6 +769,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1002,6 +1012,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1089,6 +1100,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1119,6 +1131,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1152,6 +1165,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1190,6 +1204,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1225,6 +1240,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1271,6 +1287,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1282,6 +1299,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1317,6 +1335,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1328,6 +1347,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1366,6 +1386,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1384,6 +1405,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1404,6 +1426,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1424,6 +1447,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "int64Add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1451,6 +1475,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqPackage E.Fns.Package.MyAdd.hash + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1469,6 +1494,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqPackage E.Fns.Package.MyAdd.hash + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1489,6 +1515,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqPackage E.Fns.Package.MyAdd.hash + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1523,6 +1550,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "add" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1538,6 +1566,7 @@ module Expr = { name = RT.FQFnName.fqPackage E.Fns.Package.MyFnThatTakesALambda.hash + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1561,6 +1590,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqPackage E.Fns.Package.Outer.hash + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -1599,6 +1629,7 @@ module Expr = RT.DApplicable( RT.AppNamedFn { name = RT.FQFnName.fqBuiltin "printLine" 0 + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } diff --git a/packages/darklang/languageTools/runtimeErrors.dark b/packages/darklang/languageTools/runtimeErrors.dark index e24af100da..42c46b1875 100644 --- a/packages/darklang/languageTools/runtimeErrors.dark +++ b/packages/darklang/languageTools/runtimeErrors.dark @@ -110,8 +110,8 @@ module Applications = | WrongNumberOfTypeArgsForFn of fn: FQFnName.FQFnName * expected: Int64 * actual: Int64 | CannotApplyTypeArgsMoreThanOnce | TooManyArgsForFn of fn: FQFnName.FQFnName * expected: Int64 * actual: Int64 - | FnParameterNotExpectedType of fnName : FQFnName.FQFnName * paramIndex: Int64 * paramName : String * expectedType : ValueType * expectedTypeLocation : Stdlib.Option.Option * actualType : ValueType * actualValue : Dval - | FnResultNotExpectedType of fnName : FQFnName.FQFnName * expectedType : ValueType * expectedTypeLocation : Stdlib.Option.Option * actualType : ValueType * actualValue : Dval + | FnParameterNotExpectedType of fnName : FQFnName.FQFnName * fnNameLocation : Stdlib.Option.Option * paramIndex: Int64 * paramName : String * expectedType : ValueType * expectedTypeLocation : Stdlib.Option.Option * actualType : ValueType * actualValue : Dval + | FnResultNotExpectedType of fnName : FQFnName.FQFnName * fnNameLocation : Stdlib.Option.Option * expectedType : ValueType * expectedTypeLocation : Stdlib.Option.Option * actualType : ValueType * actualValue : Dval | CannotApplyTypeArgsToLambda | TooManyArgsForLambda of lambdaExprId: ID * expected: Int64 * actual: Int64 diff --git a/packages/darklang/prettyPrinter/runtimeError.dark b/packages/darklang/prettyPrinter/runtimeError.dark index f2b55521bf..1d6687524e 100644 --- a/packages/darklang/prettyPrinter/runtimeError.dark +++ b/packages/darklang/prettyPrinter/runtimeError.dark @@ -26,6 +26,10 @@ module RuntimeError = // -- Functions | FunctionName of FQFnName + /// Renders a function name using the package location that was actually + /// referenced. `None` falls back to normal hash-based rendering. + | FunctionNameAt of + FQFnName * Stdlib.Option.Option /// Description from StdLib description fields. /// Note: may include markers like ``, to be parsed and displayed differently. | Description of String @@ -93,6 +97,10 @@ module RuntimeError = | Some prev -> Stdlib.String.articleFor prev ++ " " | FunctionName fn -> fnName branchId fn + | FunctionNameAt(fn, location) -> + match location with + | Some loc -> packageName loc.owner loc.modules loc.name + | None -> fnName branchId fn | Description d -> d | ParamName p -> $"`{p}`" @@ -160,6 +168,15 @@ module RuntimeError = : ES = ES.ExpectedType(expectedType, expectedTypeLocation) + /// Renders a function name in a mismatch, naming the function from its + /// resolved `location` when known so a structurally-identical sibling that + /// shares its hash isn't shown in its place. + let fnNameSegment + (fnName: FQFnName) + (fnNameLocation: Stdlib.Option.Option) + : ES = + ES.FunctionNameAt(fnName, fnNameLocation) + let toSegments (e: LanguageTools.RuntimeTypes.RuntimeError.Error) @@ -446,8 +463,8 @@ module RuntimeError = ES.Count(expected, ES.String "argument", ES.String "arguments") ES.String ", but got " ES.Count(actual, ES.String "argument", ES.String "arguments") ] - | FnParameterNotExpectedType(fnName, paramIndex, paramName, expectedType, expectedTypeLocation, actualType, actualValue) -> - [ ES.FunctionName fnName + | FnParameterNotExpectedType(fnName, fnNameLocation, paramIndex, paramName, expectedType, expectedTypeLocation, actualType, actualValue) -> + [ (fnNameSegment fnName fnNameLocation) ES.String "'s " ES.Ordinal(paramIndex + 1L) ES.String " parameter " @@ -459,8 +476,8 @@ module RuntimeError = ES.String " (" ES.FullValue actualValue ES.String ")" ] - | FnResultNotExpectedType(fnName, expectedType, expectedTypeLocation, actualType, actualValue) -> - [ ES.FunctionName fnName + | FnResultNotExpectedType(fnName, fnNameLocation, expectedType, expectedTypeLocation, actualType, actualValue) -> + [ (fnNameSegment fnName fnNameLocation) ES.String "'s return value expects " (expectedSegment expectedType expectedTypeLocation) ES.String ", but got "