kadena-io / pact

The Pact Smart Contract Language
https://docs.kadena.io/build/pact
BSD 3-Clause "New" or "Revised" License
583 stars 101 forks source link

Bad error message diagnosis but no clear fix. #553

Closed joelburget closed 2 years ago

joelburget commented 5 years ago

@emilypi reported this bad error message:

pact> (typecheck yieldtest.tester)
<interactive>:0:0: Unable to resolve module TDef {_tDef = Def {_dDefName = DefName "tester", _dModule = ModuleName {_mnName = "yieldtest", _mnNamespace = Nothing}, _dDefType = Defpact, _dFunType = FunType {_ftArgs = [Arg {_aName = "name", _aType = TyVar {_tyVar = TypeVar {_tvName = "b", _tvConstraint = []}}, _aInfo = name}], _ftReturn = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = []}}}, _dDefBody = Scope (TList {_tList = [TStep {_tStep = Step {_sEntity = Just (TLiteral {_tLiteral = LString {_lString = "A"}, _tInfo = "A"      }), _sExec = TBinding {_tBindPairs = [BindPair {_bpArg = Arg {_aName = "nameA", _aType = TyVar {_tyVar = TypeVar {_tvName = "c", _tvConstraint = []}}, _aInfo = nameA }, _bpVal = TApp {_tApp = App {_appFun = TVar {_tVar = F (TVar {_tVar = Direct (TNative {_tNativeName = NativeDefName "+", _tNativeFun = NativeDFun (NativeDefName "+") _, _tFunTypes = FunType {_ftArgs = [Arg {_aName = "x", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}, _aInfo = },Arg {_aName = "y", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}, _aInfo = }], _ftReturn = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}} :| [FunType {_ftArgs = [Arg {_aName = "x", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}, _aInfo = },Arg {_aName = "y", _aType = TyVar {_tyVar = TypeVar {_tvName = "b", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}, _aInfo = }], _ftReturn = TyPrim TyDecimal},FunType {_ftArgs = [Arg {_aName = "x", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyString,TyList {_tyListType = TyVar {_tyVar = TypeVar {_tvName = "l", _tvConstraint = []}}},TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "o"}}, _tySchemaPartial = FullSchema}]}}, _aInfo = },Arg {_aName = "y", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyString,TyList It's passed a{_tyListType = TyVar {_tyVar = TypeVar {_tvName = "l", _tvConstraint = []}}},TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "o"}}, _tySchemaPartial = FullSchema}]}}, _aInfo = }], _ftReturn = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyString,TyList {_tyListType = TyVar {_tyVar = TypeVar {_tvName = "l", _tvConstraint = []}}},TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "o"}}, _tySchemaPartial = FullSchema}]}}}], _tNativeExamples = [ExecExample "(+ 1 2)",ExecExample "(+ 5.0 0.5)",ExecExample "(+ \"every\" \"body\")",ExecExample "(+ [1 2] [3 4])",ExecExample "(+ { \"foo\": 100 } { \"foo\": 1, \"bar\": 2 })"], _tNativeDocs = "Add numbers, concatenate strings/lists, or merge objects.", _tNativeTopLevelOnly = False, _tInfo = }), _tInfo = }), _tInfo = + }, _appArgs = [TVar {_tVar = B 0, _tInfo = name },TLiteral {_tLiteral = LString {_lString = "->A"}, _tInfo = "->A"}], _appInfo = (+ name "->A")}, _tInfo = (+ name "->A")}}], _tBindBody = Scope (TList {_tList = [TApp {_tApp = App {_appFun = TVar {_tVar = F (TVar {_tVar = F (TVar {_tVar = Direct (TNative {_tNativeName = NativeDefName "yield", _tNativeFun = NativeDFun (NativeDefName "yield") _, _tFunTypes = FunType {_ftArgs = [Arg {_aName = "object", _aType = TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "y"}}, _tySchemaPartial = FullSchema}, _aInfo = }], _ftReturn = TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "y"}}, _tySchemaPartial = FullSchema}} :| [FunType {_ftArgs = [Arg {_aName = "object", _aType = TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "y"}}, _tySchemaPartial = FullSchema}, _aInfo = },Arg {_aName = "target-chain", _aType = TyPrim TyString, _aInfo = }], _ftReturn = TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "y"}}, _tySchemaPartial = FullSchema}}], _tNativeExamples = [LitExample "(yield { \"amount\": 100.0 })",LitExample "(yield { \"amount\": 100.0 } \"some-chain-id\")"], _tNativeDocs = "Yield OBJECT for use with 'resume' in following pact step. With optional argument TARGET-CHAIN, target subsequent step to execute on targeted chain using automated SPV endorsement-based dispatch.", _tNativeTopLevelOnly = False, _tInfo = }), _tInfo = }), _tInfo = }), _tInfo = yield }, _appArgs = [TObject {_tObject = Object {_oObject = ObjectMap {_objectMap = fromList [(FieldKey "a-result",TVar {_tVar = B 0, _tInfo = nameA})]}, _oObjectType = TyAny, _oKeyOrder = Just [FieldKey "a-result"], _oInfo = { "a-result": nameA}}, _tInfo = { "a-result": nameA}}], _appInfo = (yield { "a-result": nameA})  ...}, _tInfo = (yield { "a-result": nameA})  ...},TVar {_tVar = B 0, _tInfo = nameA}], _tListType = TyAny, _tInfo = (let ((nameA (+ name "->A"))) ...}), _tBindType = BindLet, _tInfo = (let ((nameA (+ name "->A"))) ...}, _sRollback = Just (TLiteral {_tLiteral = LString {_lString = "rollback-a"}, _tInfo = "rollback-a"}), _sInfo = (step-with-rollback      "A"  ...}, _tInfo = (step-with-rollback      "A"  ...},TStep {_tStep = Step {_sEntity = Just (TLiteral {_tLiteral = LString {_lString = "B"}, _tInfo = "B"       }), _sExec = TApp {_tApp = App {_appFun = TVar {_tVar = F (TVar {_tVar = Direct (TNative {_tNativeName = NativeDefName "resume", _tNativeFun = NativeDFun (NativeDefName "resume") _, _tFunTypes = FunType {_ftArgs = [Arg {_aName = "binding", _aType = TySchema {_tySchema = TyBinding, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "r"}}, _tySchemaPartial = FullSchema}, _aInfo = }], _ftReturn = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = []}}} :| [], _tNativeExamples = [], _tNativeDocs = "Special form binds to a yielded object value from the prior step execution in a pact. If yield step was executed on a foreign chain, enforce endorsement via SPV.", _tNativeTopLevelOnly = False, _tInfo = }), _tInfo = }), _tInfo = resume }, _appArgs = [TBinding {_tBindPairs = [BindPair {_bpArg = Arg {_aName = "ar", _aType = TyVar {_tyVar = TypeVar {_tvName = "d", _tvConstraint = []}}, _aInfo = ar }, _bpVal = TLiteral {_tLiteral = LString {_lString = "a-result"}, _tInfo = "a-result" }}], _tBindBody = Scope (TList {_tList = [TApp {_tApp = App {_appFun = TVar {_tVar = F (TVar {_tVar = F (TVar {_tVar = Direct (TNative {_tNativeName = NativeDefName "+", _tNativeFun = NativeDFun (NativeDefName "+") _, _tFunTypes = FunType {_ftArgs = [Arg {_aName = "x", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}, _aInfo = },Arg {_aName = "y", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}, _aInfo = }], _ftReturn = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}} :| [FunType {_ftArgs = [Arg {_aName = "x", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}, _aInfo = },Arg {_aName = "y", _aType = TyVar {_tyVar = TypeVar {_tvName = "b", _tvConstraint = [TyPrim TyInteger,TyPrim TyDecimal]}}, _aInfo = }], _ftReturn = TyPrim TyDecimal},FunType {_ftArgs = [Arg {_aName = "x", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyString,TyList {_tyListType = TyVar {_tyVar = TypeVar {_tvName = "l", _tvConstraint = []}}},TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "o"}}, _tySchemaPartial = FullSchema}]}}, _aInfo = },Arg {_aName = "y", _aType = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyString,TyList {_tyListType = TyVar {_tyVar = TypeVar {_tvName = "l", _tvConstraint = []}}},TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "o"}}, _tySchemaPartial = FullSchema}]}}, _aInfo = }], _ftReturn = TyVar {_tyVar = TypeVar {_tvName = "a", _tvConstraint = [TyPrim TyString,TyList {_tyListType = TyVar {_tyVar = TypeVar {_tvName = "l", _tvConstraint = []}}},TySchema {_tySchema = TyObject, _tySchemaType = TyVar {_tyVar = SchemaVar {_tvName = "o"}}, _tySchemaPartial = FullSchema}]}}}], _tNativeExamples = [ExecExample "(+ 1 2)",ExecExample "(+ 5.0 0.5)",ExecExample "(+ \"every\" \"body\")",ExecExample "(+ [1 2] [3 4])",ExecExample "(+ { \"foo\": 100 } { \"foo\": 1, \"bar\": 2 })"], _tNativeDocs = "Add numbers, concatenate strings/lists, or merge objects.", _tNativeTopLevelOnly = False, _tInfo = }), _tInfo = }), _tInfo = }), _tInfo = + }, _appArgs = [TVar {_tVar = B 0, _tInfo = ar },TLiteral {_tLiteral = LString {_lString = "->B"}, _tInfo = "->B"}], _appInfo = (+ ar "->B")}, _tInfo = (+ ar "->B")}], _tListType = TyAny, _tInfo = (resume { "a-result" := ar }  ...}), _tBindType = BindSchema {_bType = TyAny}, _tInfo = { "a-result" := ar }         }], _appInfo = (resume { "a-result" := ar }  ...}, _tInfo = (resume { "a-result" := ar }  ...}, _sRollback = Nothing, _sInfo = (step "B"       (resume { "a-r...}, _tInfo = (step "B"       (resume { "a-r...}], _tListType = TyAny, _tInfo = (defpact tester (name)     (st...}), _dMeta = Meta {_mDocs = Nothing, _mModel = []}, _dInfo = (defpact tester (name)     (st...}, _tInfo = (defpact tester (name)     (st...}
 at <interactive>:0:0: (typecheck (defpact yieldtest.tester:<a> (name:<b>)))
pact> 

The first thing to note is that pact thinks the module name is actually "TDef {_tDef =...". How is this possible? Are you sure you want to know?

The second clue is that typecheck is meant to be called with a string argument (eg (typecheck 'module)). As an aside, this raises the question of how to typecheck qualified modules like Emily wanted to do. Is this possible? I don't know.

Going to the definition of typecheck, we see defZRNative "typecheck" tc .... The key is that we're defining it via defZRNative, which reduces all of its arguments. What does reduce do when passed a TDef?

reduce t@TDef {} = return $ toTerm $ pack $ show t
reduce t@TNative {} = return $ toTerm $ pack $ show t

So the module passed in is shown, resulting in the error message.

I don't know what the right fix for this is. The obvious place to start would be replacing show with pretty in reduce. But why would a definition or native be reduced anyway? It's nonsensical. We could change typecheck to not reduce its arguments, but reduce changes types (reduce :: Term Ref -> Eval e (Term Name)), so I'm not sure how to make this typecheck.

sirlensalot commented 5 years ago

The obvious place to start would be replacing show with pretty in reduce. But why would a definition or native be reduced anyway? It's nonsensical.

We could simply throw an error on this and other Terms for which a Term Name is meaningless. The upshot would be we'd want top-level eval to handle these and return the string stuff for use in the repl.

joelburget commented 5 years ago

The upshot would be we'd want top-level eval to handle these and return the string stuff for use in the repl.

In the commit I just pushed I wasn't sure how to handle this. There are two cases to consider -- natives (eg +) and definitions (eg yieldtest.tester).

In the native case, things still work:

pact> +
native `+`

  Add numbers, concatenate strings/lists, or merge objects.

  ...

This is because + is represented as a TVar, so reduce just calls deref, which returns the TNative, which is pretty-printed.

In the definition case, we throw an error:

pact> yieldtest.tester
tests/pact/yield.repl:12:2: Unexpected evaluation of a definition (tester)

This is because (like in the native case) we're attempting to reduce a TVar. This time however, we dereference the var and attempt to reduce it. Would it work for eval to, when passed a var, first dereference it, check if it's a TDef, and in that case, show the definition? This is a bit beyond my understanding of the evaluation mechanism.