diff --git a/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs b/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs index 725dabff76..5a555fd397 100644 --- a/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs +++ b/backend/src/Builtins/Builtins.Http.Client/Libs/HttpClient.fs @@ -689,9 +689,11 @@ let fns (config : Configuration) : List = FQFnName.fqPackage ( PackageRefs.Fn.Stdlib.HttpClient.request () ), + None, 2, "headers", VT.list (VT.tuple VT.string VT.string []), + None, Dval.toValueType notAPair, notAPair ) @@ -821,9 +823,11 @@ let fns (config : Configuration) : List = FQFnName.fqPackage ( PackageRefs.Fn.Stdlib.HttpClient.stream () ), + None, 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/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/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/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 e6eb30a2b1..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,7 +1139,9 @@ 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 = @@ -745,6 +751,7 @@ module Expr = // Create a named function reference to the current function let namedFn : RT.ApplicableNamedFn = { name = FQFnName.toRT fnName + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -872,6 +879,7 @@ module Expr = right.registerCount, RT.AppNamedFn { name = InfixFnName.toFnName infix |> RT.FQFnName.Builtin + location = None typeSymbolTable = Map.empty typeArgs = [] argsSoFar = [] } @@ -913,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 8e6b5ff2a5..871b0d79f7 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. @@ -579,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 = { @@ -994,15 +1015,27 @@ 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 * + // 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 * + // See `fnNameLocation` on `FnParameterNotExpectedType`. + fnNameLocation : Option * expectedType : ValueType * + // See `expectedTypeLocation` on `FnParameterNotExpectedType`. + expectedTypeLocation : Option * actualType : ValueType * actualValue : Dval @@ -1644,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 @@ -1759,6 +1799,7 @@ type VMState = let rootCallFrame : CallFrame = { id = rootCallFrameID executionPoint = Source + fnLocation = None programCounter = 0 registers = Array.zeroCreate instrs.registerCount typeSymbolTable = Map.empty @@ -2015,14 +2056,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 ] ) @@ -2039,6 +2082,23 @@ 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 + | _ -> [] + + /// 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) (tst : TypeSymbolTable) diff --git a/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs b/backend/src/LibExecution/RuntimeTypesToDarkTypes.fs index ec1e844dac..ea5611932f 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" [] @@ -572,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 } @@ -1220,25 +1249,45 @@ module RuntimeError = | RuntimeError.Applications.TooManyArgsForFn(fn, expected, actual) -> "TooManyArgsForFn", [ FQFnName.toDT fn; DInt64 expected; DInt64 actual ] | RuntimeError.Applications.FnParameterNotExpectedType(fnName, + fnNameLocation, paramIndex, paramName, expectedType, + expectedTypeLocation, actualType, actualValue) -> "FnParameterNotExpectedType", [ FQFnName.toDT fnName + C2DT.Option.toDT + PackageLocation.toDT + (PackageLocation.knownType ()) + fnNameLocation DInt64 paramIndex DString paramName ValueType.toDT expectedType + C2DT.Option.toDT + PackageLocation.toDT + (PackageLocation.knownType ()) + expectedTypeLocation 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 + (PackageLocation.knownType ()) + expectedTypeLocation ValueType.toDT actualType Dval.toDT actualValue ] @@ -1280,12 +1329,21 @@ module RuntimeError = _, [], "FnParameterNotExpectedType", - [ fnName; paramIndex; paramName; expectedType; actualType; actualValue ]) -> + [ fnName + fnNameLocation + paramIndex + paramName + expectedType + expectedTypeLocation + actualType + actualValue ]) -> RuntimeError.Applications.FnParameterNotExpectedType( FQFnName.fromDT fnName, + C2DT.Option.fromDT PackageLocation.fromDT fnNameLocation, D.int64 paramIndex, D.string paramName, ValueType.fromDT expectedType, + C2DT.Option.fromDT PackageLocation.fromDT expectedTypeLocation, ValueType.fromDT actualType, Dval.fromDT actualValue ) @@ -1293,10 +1351,17 @@ module RuntimeError = _, [], "FnResultNotExpectedType", - [ fnName; expectedType; 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, Dval.fromDT actualValue ) diff --git a/backend/src/LibExecution/TypeChecker.fs b/backend/src/LibExecution/TypeChecker.fs index 69d7652364..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) @@ -251,6 +252,7 @@ let checkFnParam (actual : Dval) : Ply> = uply { + let expectedLocation = TypeReference.locationOf expected let! expected = TypeReference.unwrapAlias types expected match! unify types tst expected actual with | Ok updatedTst -> return Ok updatedTst @@ -259,9 +261,11 @@ let checkFnParam return RTE.Applications.FnParameterNotExpectedType( fnName, + fnNameLocation, paramIndex, paramName, expected, + expectedLocation, Dval.toValueType actual, actual ) @@ -273,11 +277,13 @@ let checkFnParam let checkFnResult (types : Types) (fnName : FQFnName.FQFnName) + (fnNameLocation : Option) (tst : TypeSymbolTable) (expected : TypeReference) (actual : Dval) : Ply> = uply { + 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 @@ -286,7 +292,9 @@ let checkFnResult return RTE.Applications.FnResultNotExpectedType( fnName, + fnNameLocation, expectedVT, + 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/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 0b04580f15..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 * actualType : ValueType * actualValue : Dval - | FnResultNotExpectedType of fnName : FQFnName.FQFnName * expectedType : ValueType * 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 a749411732..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 @@ -38,6 +42,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 @@ -90,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}`" @@ -102,6 +113,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,6 +158,26 @@ module RuntimeError = | _ -> "" + /// 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) + + /// 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) : ErrorSegments = @@ -431,23 +463,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) -> - [ 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 " ES.ParamName paramName ES.String " expects " - ES.ValueType expectedType + (expectedSegment expectedType expectedTypeLocation) ES.String ", but got " ES.ValueType actualType ES.String " (" ES.FullValue actualValue ES.String ")" ] - | FnResultNotExpectedType(fnName, expectedType, actualType, actualValue) -> - [ ES.FunctionName fnName + | FnResultNotExpectedType(fnName, fnNameLocation, expectedType, expectedTypeLocation, actualType, actualValue) -> + [ (fnNameSegment fnName fnNameLocation) ES.String "'s return value expects " - ES.ValueType expectedType + (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 ->