-
Notifications
You must be signed in to change notification settings - Fork 479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better errors for literal ranges in PlutusTx (PLT-8174) #5619
Conversation
Delete accidentally-added file
…tput-hk/plutus into kwxm/plugin/better-range-errors
plutus-tx-plugin/test/TH/Spec.hs
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't change anything in this file but appear to have reformatted it accidentally. I don't think it'll do any harm.
enumFromTo x y | ||
| x > y = [] | ||
| otherwise = x : enumFromTo (succ x) y | ||
enumFromTo x lim |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed y
to lim
so that it looks more like what's in GHC.Enum
. That changed a lot of golden files too.
@@ -200,12 +200,12 @@ let | |||
in | |||
letrec | |||
!`$fEnumBool_$cenumFromTo` : integer -> integer -> List integer | |||
= \(x : integer) (y : integer) -> | |||
= \(x : integer) (lim : integer) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See later for an explanation of why all of these have changed.
@@ -115,7 +115,7 @@ executable gen-plugin-opts-doc | |||
|
|||
default-language: Haskell2010 | |||
|
|||
test-suite plutus-tx-tests | |||
test-suite plutus-tx-plugin-tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this because plutus-tx
contains plutus-tx-test
and I was getting very confused.
| Just m <- GHC.nameModule_maybe n | ||
, GHC.moduleNameString (GHC.moduleName m) == "GHC.Enum" = | ||
("$fEnum" `isPrefixOf` methodName && | ||
( "_$cenumFromTo" `isSuffixOf` methodName -- [1..100] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any better way of getting names of particular instances of methods like enumFromTo
? This was the best I could do and I'm not sure how robust it will be. I suppose that if the names that GHC produces change then the tests will start to fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, this is too bad. I hadn't realized that GHC might specialize enumFromTo
.
The answer to your question is, unfortunately, no. There's no way to prevent GHC from specializing a method. It is only possible to disable user written rules, not builtin rules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should match on the specialized name. I don't know if we can guarantee that that isn't an arbitrarily messed up version 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We pass some information about GHC names through to the compiler in NameInfo
in the CompileContext
. Then you can use getThing
to get the TypedThing
and getName
on that to get the Name
. See how the stuff in Builitns
works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We pass some information about GHC names through to the compiler in
NameInfo
in theCompileContext
. Then you can usegetThing
to get theTypedThing
andgetName
on that to get theName
. See how the stuff inBuilitns
works.
Maybe we can come back to this in a later PR. I've run out of JIRA points on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should match on the specialized name. I don't know if we can guarantee that that isn't an arbitrarily messed up version 🤔
That's what I was asking about in my original comment. Can we actually get at the original name, especially after the inlining/rewriting? Is that what NameInfo
is about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, NameInfo
is just for getting an actual GHC Name
instead of string matching. I'm not sure it will match up if it has in fact been specialized. I'm not sure what we do in other cases, perhaps we just fail to spot stuff 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's not too bad since we're failing instead of silently compiling differently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess since this is inherently heuristic rather than matching a precise name there's no point doing the NameInfo
stuff and this is fine.
The Edit: the budget increase seems to be due to the addition of Edit: I've updated the test results, but we could still remove |
@@ -2,7 +2,6 @@ | |||
{-# LANGUAGE OverloadedStrings #-} | |||
{-# LANGUAGE TemplateHaskell #-} | |||
|
|||
{-# OPTIONS_GHC -fplugin PlutusTx.Plugin #-} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing to do with this PR: I'm just taking the opportunity to remove it while I'm here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, I'm going to revert that. It may be causing problems with at least one GHC version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good.
| Just m <- GHC.nameModule_maybe n | ||
, GHC.moduleNameString (GHC.moduleName m) == "GHC.Enum" = | ||
("$fEnum" `isPrefixOf` methodName && | ||
( "_$cenumFromTo" `isSuffixOf` methodName -- [1..100] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, this is too bad. I hadn't realized that GHC might specialize enumFromTo
.
The answer to your question is, unfortunately, no. There's no way to prevent GHC from specializing a method. It is only possible to disable user written rules, not builtin rules.
-- by looking for occurrences of GHC.Enum.enumFromTo and similar functions; the same error | ||
-- occurs if these functions are used explicitly. | ||
| isProbablyBoundedRange n -> | ||
throwPlain $ UnsupportedError "Literal ranges are not supported: please use PlutusTx.Enum.enumFromTo or PlutusTx.Enum.enumFromThenTo" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't know for sure that the user used list range syntax. You can instead say something like: "Unsupported feature: GHC.Enum.Enum. You probably used list range syntax, which is not currently supported. You can use PlutusTx.Enum.Enum instead".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't know for sure that the user used list range syntax.
Yes, I did worry about that but decided to go for the shorter version to avoid a giant string in the source code. I'll change it.
|
||
-- Tests for literal ranges (and the corresponding methods in GHC.Enum). These | ||
-- should all fail with informative error messages. | ||
rangeEnumFromTo :: CompiledCode [Integer] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice tests.
| Just m <- GHC.nameModule_maybe n | ||
, GHC.moduleNameString (GHC.moduleName m) == "GHC.Enum" = | ||
("$fEnum" `isPrefixOf` methodName && | ||
( "_$cenumFromTo" `isSuffixOf` methodName -- [1..100] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should match on the specialized name. I don't know if we can guarantee that that isn't an arbitrarily messed up version 🤔
| Just m <- GHC.nameModule_maybe n | ||
, GHC.moduleNameString (GHC.moduleName m) == "GHC.Enum" = | ||
("$fEnum" `isPrefixOf` methodName && | ||
( "_$cenumFromTo" `isSuffixOf` methodName -- [1..100] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We pass some information about GHC names through to the compiler in NameInfo
in the CompileContext
. Then you can use getThing
to get the TypedThing
and getName
on that to get the Name
. See how the stuff in Builitns
works.
-- | Construct a list from the given range (corresponds to [a,b..c]). This | ||
-- has the same semantics as the Haskell version,so if a==b and c>=b then you | ||
-- get an infinite list, which you probably don't want in Plutus Core. | ||
enumFromThenTo :: a -> a -> a -> [a] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should add this. It's not that useful and it makes the dictionaries bigger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should add this. It's not that useful and it makes the dictionaries bigger.
Yeah, I wondered about that. I thought I might as well add it while I was looking at this, but it didn't initially occur to me that it would make the dictionary bigger.
I think I'd like to merge this to get it out of the way. There are a number of things I'd like to revisit at a later date though: see the original PR comments. |
@kwxm @michaelpj Have you looked into using RebindableSyntax instead? From https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/rebindable_syntax.html :
It should make the list notation work for PlutusTx, at least on paper. |
[See also issues #5589 and #3395].
Suppose you have this code in a Haskell program:
The plugin currently gives you a 186-line error message like this:
This happens because the range is desugared to a call to enumFromTo in the
Enum
class, but things are complicated by the fact that in the case ofInteger
there is extensive inlining and rewriting.This PR improves things slightly by spotting uses of
enumFromTo
and related functions and suggesting that the user should usePlutusTx.enumFromTo
instead. I've also addedPlutusTx.enumFromThenTo
for ranges like[1, 5..101]
with non-unit steps.With this change the above error message is replaced by
This is an improvement, but is still not ideal: if the range is nested inside multiple functions then you'll still get lots of nested contexts with the final line (which is the only one containing a source location) pointing to the splice, which may be a long way from where the range occurs. I think we should merge this for now and think about ways to improve the situation.
Possible improvements
I thought about getting the plugin to replace occurrences of
enumFromTo
withPlutusTx.enumFromTo
. This is tricky for a number of reasons:Integer
some other function may be called and it's difficult to work out what's going on.PlutusTx.enumFromTo
: those are quite complicated (they include unique IDs, for example) and I'm not sure how to get hold of one.I looked at trying to improve the context information as well, but it looked as if that'd take quite a lot of work.
PlutusTx.Plugin:context-level
option which looks as if it should let you change the amount of context information, but I couldn't get it to work. I think it may be getting overridden by calls totraceCompilation
like this.