You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: courses/RascalAmendmentProposals/RAP9/RAP9.md
+56-45Lines changed: 56 additions & 45 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,40 +12,48 @@ sidebar_position: 9
12
12
13
13
## Abstract
14
14
15
-
Proposal to merge the (open-ended) data-types for Rascal exceptions, Java exceptions, error messages, assert failures and test results into a single common representation.
15
+
Proposal to merge the (open-ended) data-types for:
16
+
* Rascal exceptions,
17
+
* Java exceptions,
18
+
* error messages,
19
+
* assert failures, and
20
+
* test results,
16
21
17
-
The representation features a cause tree, source locations, context parameters and user-friendly messages. This new representation can be used to \`throw\` exceptions, collect static error messages and warnings, and report on test results. All in the same unified manner.
22
+
into a single common representation.
23
+
24
+
The representation features a cause tree, source locations, context parameters and user-friendly messages. This new representation can be used to `throw` exceptions, collect static error messages and warnings, and report on test results. All in the same unified manner.
18
25
19
26
Different back-end tools can be reused based on this shared representation, such as UI (error panes, highlights, stack traces) and analysis (root cause analysis, statistical debugging).
20
27
21
28
## Motivation
22
29
23
-
Exceptions are modeled as values in Rascal. In the standard library we have a common data-type called \`RuntimeException\` for them. A special kind of exception is reserved for assertion failures. Then we have \`Message\` to represent errors (info, warning, error, fatal), and we have test results (hidden inside the quick check feature). Finally normal Java exceptions also play an important role in our runtime which are now all mapped to the \`Java(str message)\` Exception constructor.
30
+
Exceptions are modeled as values in Rascal. In the standard library we have a common data-type called `RuntimeException` for them. A special kind of exception is reserved for assertion failures. Then we have `Message` to represent errors (info, warning, error, fatal), and we have test results (hidden inside the quick check feature). Finally normal Java exceptions also play an important role in our runtime which are now all mapped to the `Java(str message)` Exception constructor.
24
31
25
32
These formats all share characteristics:
26
33
27
34
* They are an intermediate representation between some thing that happened and its reporting in the UI;
28
35
* They each require user reporting, causal tracing and analysis;
29
-
* They are all \`open\` ended, in the sense that many types of exceptions, errors and test results will exist which can not be thought of in advance.
36
+
* They are all _open-ended_, in the sense that many types of exceptions, errors and test results will exist which can not be thought of in advance.
30
37
31
38
In the interest of simplicity and reuse (of representation, of analysis and of UI components), we can unify these representations into a single data-type. This will remove unnecessary code from different kinds of tools and enable newbies to start off with something that has everything they needed before they even thought of it.
32
39
33
40
## Specification
34
41
35
-
data Severity \= info() | warning() | error() | fatal();
42
+
```
43
+
data Severity = info() | warning() | error() | fatal();
36
44
37
45
@synopsis{An event is something to throw or report which has a severity, a source location, a cause, and a user-friendly message. Events kinds are modelled using an open-ended set of constructors of the data-type Event}
38
46
data Event(
39
-
Severity severity \= info(),
40
-
loc src \= |unknown:|,
41
-
Event cause \= root(),
42
-
str message \= “”
47
+
Severity severity = info(),
48
+
loc src = |unknown:|,
49
+
Event cause = root(),
50
+
str message = “”
43
51
);
44
52
45
53
@synopsis{Events can be composed using boolean logic to explain their causes}
46
54
data Event
47
-
\= and(list\[Event\] causes) // all these causes are required to produce the event
48
-
| or(list\[Event\] causes) // either one of these caused were required
55
+
= and(list[Event] causes) // all these causes are required to produce the event
56
+
| or(list[Event] causes) // either one of these caused were required
49
57
| root() // this is the root cause of the event
50
58
| not(Event cause) // the absence of this event caused the event
@synopsis{Events to model static errors and warnings}
77
85
data Event
78
-
\= unexpectedType(Type got, Type expected, Severity severity=error())
86
+
= unexpectedType(Type got, Type expected, Severity severity=error())
79
87
| undeclaredName(str name, Severity=error())
80
-
; /\* etc \*/
88
+
; /* etc */
81
89
82
90
@synopsis{Events to model run-time errors}
83
91
data Event
84
-
\= matchFailed(Severity severity=warning())
92
+
= matchFailed(Severity severity=warning())
85
93
| divisionByZero(Severity severity=error())
86
94
| permissionDenied(Severity severity=error())
87
-
; /\* etc \*/
95
+
; /* etc */
96
+
```
88
97
89
98
1. a “stack trace” is modeled via the “cause” of an event.
90
-
1. The \`throw\` statement, when confronted with an expression of sub-type \`node\`, will re-ify the stack by adding to the\`causes\` field to the element \`functionCall(...)\` that is the current function with its current parameters.
91
-
1. If the Event thrown already has user-defined causes, then these are kept as conjunctive causes. For example if the current throw is part of a catch block, the programmer might link the caught Event into the current Event by setting: \`catch Event caughtEvent: { throw newEvent(cause=caughtEvent\`); }\`
99
+
1. The `throw` statement, when confronted with an expression of sub-type `node`, will re-ify the stack by adding to the`causes` field to the element `functionCall(...)` that is the current function with its current parameters.
100
+
1. If the Event thrown already has user-defined causes, then these are kept as conjunctive causes. For example if the current throw is part of a catch block, the programmer might link the caught Event into the current Event by setting: `catch Event caughtEvent: { throw newEvent(cause=caughtEvent`); }`
92
101
2. Every functionCall on the causes chain has its own singleton cause, namely its own caller.
93
-
3.\`throw\` will also generate the \`src\` location of itself into the Event, and also annotate functionCall and methodCall values with this information where possible.
94
-
4. the causes of test failure may be the \`expected\*\` events or an exception thrown during the execution of the test, or both.
102
+
3.`throw` will also generate the `src` location of itself into the Event, and also annotate functionCall and methodCall values with this information where possible.
103
+
4. the causes of test failure may be the `expected*` events or an exception thrown during the execution of the test, or both.
95
104
2. static error messages may be caused by observations or inferences from the source code rather than run-time events.
96
105
1. The Event data-type may wrap arbitrary complex symbols to represent the results of name and type analysis which are relevant to explain an error message.
97
-
2. The \`not\` event can be used that some errors are caused by the absence of something rather than the presence. For example: \`not(subTypeOf(x, y))\` explains that an error is caused by a required event (x being a subtype of y) has not happened.
98
-
3. Errors may also be linked in this way, and repeated errors can be cleaned up if they are caused by other errors, to prevent spamming the user. This clean up can be done generically based on a list of Event’s rather than the checker having to manage these dependencies explicitly. All the checker has to do is to record meticulously what causes every error.
99
-
3. The standard attributes of \`Events\` can be produced by automatically \`throw\` but also programmatically provided (e.g. by a type checker or test runner). This makes no difference for the downstream processing of Events.
100
-
4. We have to rewrite all existing Messages, Exceptions and test result representations
106
+
2. The `not` event can be used that some errors are caused by the absence of something rather than the presence. For example: `not(subTypeOf(x, y))` explains that an error is caused by a required event (x being a subtype of y) has not happened.
107
+
3. Errors may also be linked in this way, and repeated errors can be cleaned up if they are caused by other errors, to prevent spamming the user. This clean up can be done generically based on a list of Event’s rather than the checker having to manage these dependencies explicitly. All the checker has to do is to record meticulously what causes every error. See Phd thesis of Bastiaan Heeren.
108
+
3. The standard attributes of `Events` can be produced by automatically `throw` but also programmatically provided (e.g. by a type checker or test runner). This makes no difference for the downstream processing of Events.
109
+
4. We would rewrite all existing Messages, Exceptions and test result representations
101
110
1. Declarations in Message, Exception
102
111
2. Representations in QuickCheck
103
112
5. Java exceptions are modeled as constructors of Event as well. If not known in advance they are declared and generated at run-time using Java reflection.
104
113
1. the name of the event constructor being the simple name of the Java Throwable class: e.getClass().getSimpleName().
105
-
2. the message set to \`e.getMessage()\`
106
-
3. the causes set to top of the stack-trace, recursively to be represented by \`methodCall\` Event constructors
107
-
4. The Java \`getCause()\` if not \`null\` is a conjunctive cause of the top exception, so if that exists a Java exception will have both a stacktrace of its own, and another exception as causes.
114
+
2. the message set to `e.getMessage()`
115
+
3. the causes set to top of the stack-trace, recursively to be represented by `methodCall` Event constructors
116
+
4. The Java `getCause()` if not `null` is a conjunctive cause of the top exception, so if that exists a Java exception will have both a stacktrace of its own, and another exception as causes.
108
117
109
118
## Examples
110
119
111
-
data Event \= insufficientGiniDatapoints(str message=”The Gini coefficient requires at least three datapoints to make sense”); // default keyword parameter links event kind to user-friendly message
120
+
```
121
+
data Event = insufficientGiniDatapoints(str message=”The Gini coefficient requires at least three datapoints to make sense”); // default keyword parameter links event kind to user-friendly message
112
122
113
-
int giniCoefficient(list\[num\] data) {
114
-
if (size(data) \<= 2\)
123
+
int giniCoefficient(list[num] data) {
124
+
if (size(data) <= 2\)
115
125
throw insufficientGiniDatapoints(); // throw fills in causality (trace, location)
116
126
…
117
127
}
118
128
119
-
data Event \= failedTodo(str title, str message=”the task \<title\> failed to complete”);
129
+
data Event = failedTodo(str title, str message=”the task <title\> failed to complete”);
120
130
121
-
void process(list\[TODO\] x) {
122
-
for (t \<- todo) {
131
+
void process(list[TODO] x) {
132
+
for (t <- todo) {
123
133
try {
124
134
t.task();
125
135
}
126
136
catch Event e : {
127
-
throw failedTodo(t.title, causes=\[e\]); // link the cause and the stack trace
137
+
throw failedTodo(t.title, causes=[e]); // link the cause and the stack trace
128
138
}
129
139
}
130
140
}
131
141
132
-
data Event \= unexpectedType(str exp, Type actual, Type expected, Severity=error(), str message=”\<exp\> requires parameters of type \<expected\>, but we have a \<actual\> here.” );
data Event = unexpectedType(str exp, Type actual, Type expected, Severity=error(), str message=”<exp\> requires parameters of type <expected\>, but we have a <actual\> here.” );
* Code generated for the throw statement needs changing
149
-
* Code generated for \`java\` methods needs to be wrapped with a try-catch, such that the stack trace can be reified as \`methodCall\` events, and the exception can be modeled as a new constructor of Event using reflection. E.g:
160
+
* Code generated for `java` methods needs to be wrapped with a try-catch, such that the stack trace can be reified as `methodCall` events, and the exception can be modeled as a new constructor of Event using reflection. E.g:
150
161
* catch (Throwable e) {
151
-
* C \= tf.constructor(Event, e.getClass().getSimpleName());
162
+
* C = tf.constructor(Event, e.getClass().getSimpleName());
* Reifing the stack trace as Event::methodCall and Event::functionCall IConstructors may be quite an expensive operation.
155
166
* It could be worth the trouble to implement IConstructor again especially for these two constructors, and let them wrap a JVM exception trace for on-demand reification.
156
-
* And lazily produce the next wrapper for their \`causes\` on request (i.e. lazily implementing \`getParamer\` or \`getField\`)
157
-
* Possibly a generic \`*LazyConstructor* class *implements**IConstructor\`* could be added to Vallang which would take Producer\<IValue\> as constructor parameters rather than IValues directly, and a \`*LazyKeywordParameterWrapper implements IWithKeywordParameters\`*, likewise would take Producer\<IValue\> lambda’s.
167
+
* And lazily produce the next wrapper for their `causes` on request (i.e. lazily implementing `getParamer` or `getField`)
168
+
* Possibly a generic `*LazyConstructor* class *implements* *IConstructor`* could be added to Vallang which would take Producer<IValue\> as constructor parameters rather than IValues directly, and a `*LazyKeywordParameterWrapper implements IWithKeywordParameters`*, likewise would take Producer<IValue\> lambda’s.
158
169
* There is no way I know of to retrieve method parameter values from Java stack traces; except:
0 commit comments