Skip to content

Commit 809109a

Browse files
guidotackcyderize
authored andcommitted
Print enum values in stack traces. Fixes #912.
1 parent 5c53a49 commit 809109a

File tree

8 files changed

+133
-94
lines changed

8 files changed

+133
-94
lines changed

changes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Bug fixes:
3131
result in a segfault (:bugref:`901`).
3232
- Fix a problem where an operator such as ``<=`` on an optional type would
3333
sometimes lead to an internal compiler error (:bugref:`898`).
34+
- Print enum values in stack traces (:bugref:`912`).
3435

3536
.. _v2.9.2:
3637

include/minizinc/prettyprinter.hh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& o
145145

146146
void pp_floatval(std::ostream& os, const FloatVal& fv, bool hexFloat = false);
147147

148+
std::string show_enum_type(EnvI& env, Expression* e, Type t, bool dzn, bool json);
149+
150+
std::string show_with_type(EnvI& env, Expression* exp, Type t, bool showDzn);
151+
148152
} // namespace MiniZinc
149153

150154
void debugprint(const MiniZinc::Expression* e);

include/minizinc/stackdump.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class Expression;
2525
class StackDump {
2626
public:
2727
StackDump(EnvI& env);
28-
StackDump() {}
28+
StackDump() : _env(nullptr) {}
2929
void print(std::ostream& os) const;
3030
void json(std::ostream& os) const;
3131
bool empty() const { return _stack.empty(); }
@@ -37,6 +37,7 @@ public:
3737
}
3838

3939
private:
40+
EnvI* _env;
4041
std::vector<std::pair<Expression*, bool>> _stack;
4142
};
4243

lib/builtins.cpp

Lines changed: 1 addition & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2333,95 +2333,6 @@ std::string b_show_index_sets(EnvI& env, Call* c) {
23332333
return oss.str();
23342334
}
23352335

2336-
std::string b_show_enum_type(EnvI& env, Expression* e, Type t, bool dzn, bool json) {
2337-
Id* ti_id = env.getEnum(t.typeId())->e()->id();
2338-
GCLock lock;
2339-
std::vector<Expression*> args(3);
2340-
args[0] = e;
2341-
if (Expression::type(e).dim() > 1) {
2342-
Call* array1d = Call::a(Location().introduce(), env.constants.ids.array1d, {e});
2343-
Type array1dt = Type::arrType(env, Type::partop(1), t);
2344-
array1d->type(array1dt);
2345-
array1d->decl(env.model->matchFn(env, array1d, false, true));
2346-
args[0] = array1d;
2347-
}
2348-
args[1] = env.constants.boollit(dzn);
2349-
args[2] = env.constants.boollit(json);
2350-
ASTString enumName(create_enum_to_string_name(ti_id, "_toString_"));
2351-
auto* call = Call::a(Location().introduce(), enumName, args);
2352-
auto* fi = env.model->matchFn(env, call, false, true);
2353-
call->decl(fi);
2354-
Expression::type(call, Type::parstring());
2355-
return eval_string(env, call);
2356-
}
2357-
2358-
std::string show_with_type(EnvI& env, Expression* exp, Type t, bool showDzn) {
2359-
GCLock lock;
2360-
Expression* e = follow_id_to_decl(exp);
2361-
if (auto* vd = Expression::dynamicCast<VarDecl>(e)) {
2362-
if ((vd->e() != nullptr) && !Expression::isa<Call>(vd->e())) {
2363-
e = vd->e();
2364-
} else {
2365-
e = vd->id();
2366-
}
2367-
}
2368-
if (Expression::type(e).isPar()) {
2369-
e = eval_par(env, e);
2370-
}
2371-
if (Expression::type(e).dim() > 0 || Expression::type(e).structBT()) {
2372-
e = eval_array_lit(env, e);
2373-
}
2374-
if (Expression::type(e).isPar() && Expression::type(e).dim() == 0 && t.bt() == Type::BT_INT &&
2375-
t.typeId() != 0) {
2376-
return b_show_enum_type(env, e, t, showDzn, false);
2377-
}
2378-
std::ostringstream oss;
2379-
if (auto* al = Expression::dynamicCast<ArrayLit>(e)) {
2380-
auto al_t = t;
2381-
if (al->isTuple() && env.getTransparentType(t) != t) {
2382-
// Unwrap nested array type
2383-
al = eval_array_lit(env, (*al)[0]);
2384-
al_t = env.getTransparentType(t);
2385-
}
2386-
oss << (al->isTuple() ? "(" : "[");
2387-
if (al->type().isrecord()) {
2388-
RecordType* rt = env.getRecordType(al->type());
2389-
assert(al->size() == rt->size());
2390-
for (unsigned int i = 0; i < al->size(); i++) {
2391-
oss << Printer::quoteId(rt->fieldName(i)) << ": "
2392-
<< show_with_type(env, (*al)[i], (*rt)[i], showDzn);
2393-
if (i < al->size() - 1) {
2394-
oss << ", ";
2395-
}
2396-
}
2397-
} else if (al->type().istuple()) {
2398-
TupleType* tt = env.getTupleType(al->type());
2399-
for (unsigned int i = 0; i < al->size(); i++) {
2400-
oss << show_with_type(env, (*al)[i], (*tt)[i], showDzn);
2401-
if (i < al->size() - 1) {
2402-
oss << ", ";
2403-
}
2404-
}
2405-
if (al->size() == 1) {
2406-
oss << ",";
2407-
}
2408-
} else {
2409-
// Use element type from al_t since evaluating e may have removed the enum types
2410-
auto elemType = al_t.elemType(env);
2411-
for (unsigned int i = 0; i < al->size(); i++) {
2412-
oss << show_with_type(env, (*al)[i], elemType, showDzn);
2413-
if (i < al->size() - 1) {
2414-
oss << ", ";
2415-
}
2416-
}
2417-
}
2418-
oss << (al->isTuple() ? ")" : "]");
2419-
} else {
2420-
Printer p(oss, 0, false, &env);
2421-
p.print(e);
2422-
}
2423-
return oss.str();
2424-
}
24252336
std::string show(EnvI& env, Expression* exp) {
24262337
return show_with_type(env, exp, Expression::type(exp), false);
24272338
}
@@ -2438,7 +2349,7 @@ std::string b_show_dzn_id(EnvI& env, Call* call) {
24382349

24392350
std::string b_show_json_basic(EnvI& env, Expression* e, Type t) {
24402351
if (t.bt() == Type::BT_INT && t.typeId() != 0 && t.isPar()) {
2441-
return b_show_enum_type(env, e, t, false, true);
2352+
return show_enum_type(env, e, t, false, true);
24422353
}
24432354
std::ostringstream oss;
24442355
Printer p(oss, 0, false, &env);

lib/prettyprinter.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <minizinc/model.hh>
2121
#include <minizinc/prettyprinter.hh>
2222
#include <minizinc/type.hh>
23+
#include <minizinc/typecheck.hh>
2324

2425
#include <iomanip>
2526
#include <limits>
@@ -2612,6 +2613,96 @@ void FznJSONPrinter::print(MiniZinc::Model* m) {
26122613
<< "}\n";
26132614
}
26142615

2616+
std::string show_enum_type(EnvI& env, Expression* e, Type t, bool dzn, bool json) {
2617+
Id* ti_id = env.getEnum(t.typeId())->e()->id();
2618+
GCLock lock;
2619+
std::vector<Expression*> args(3);
2620+
args[0] = e;
2621+
if (Expression::type(e).dim() > 1) {
2622+
Call* array1d = Call::a(Location().introduce(), env.constants.ids.array1d, {e});
2623+
Type array1dt = Type::arrType(env, Type::partop(1), t);
2624+
array1d->type(array1dt);
2625+
array1d->decl(env.model->matchFn(env, array1d, false, true));
2626+
args[0] = array1d;
2627+
}
2628+
args[1] = env.constants.boollit(dzn);
2629+
args[2] = env.constants.boollit(json);
2630+
ASTString enumName(create_enum_to_string_name(ti_id, "_toString_"));
2631+
auto* call = Call::a(Location().introduce(), enumName, args);
2632+
auto* fi = env.model->matchFn(env, call, false, true);
2633+
call->decl(fi);
2634+
Expression::type(call, Type::parstring());
2635+
return eval_string(env, call);
2636+
}
2637+
2638+
std::string show_with_type(EnvI& env, Expression* exp, Type t, bool showDzn) {
2639+
GCLock lock;
2640+
Expression* e = follow_id_to_decl(exp);
2641+
if (auto* vd = Expression::dynamicCast<VarDecl>(e)) {
2642+
if ((vd->e() != nullptr) && !Expression::isa<Call>(vd->e())) {
2643+
e = vd->e();
2644+
} else {
2645+
e = vd->id();
2646+
}
2647+
}
2648+
if (Expression::type(e).isPar()) {
2649+
e = eval_par(env, e);
2650+
}
2651+
if (Expression::type(e).dim() > 0 || Expression::type(e).structBT()) {
2652+
e = eval_array_lit(env, e);
2653+
}
2654+
if (Expression::type(e).isPar() && Expression::type(e).dim() == 0 && t.bt() == Type::BT_INT &&
2655+
t.typeId() != 0) {
2656+
return show_enum_type(env, e, t, showDzn, false);
2657+
}
2658+
std::ostringstream oss;
2659+
if (auto* al = Expression::dynamicCast<ArrayLit>(e)) {
2660+
auto al_t = t;
2661+
if (al->isTuple() && env.getTransparentType(t) != t) {
2662+
// Unwrap nested array type
2663+
al = eval_array_lit(env, (*al)[0]);
2664+
al_t = env.getTransparentType(t);
2665+
}
2666+
oss << (al->isTuple() ? "(" : "[");
2667+
if (al->type().isrecord()) {
2668+
RecordType* rt = env.getRecordType(al->type());
2669+
assert(al->size() == rt->size());
2670+
for (unsigned int i = 0; i < al->size(); i++) {
2671+
oss << Printer::quoteId(rt->fieldName(i)) << ": "
2672+
<< show_with_type(env, (*al)[i], (*rt)[i], showDzn);
2673+
if (i < al->size() - 1) {
2674+
oss << ", ";
2675+
}
2676+
}
2677+
} else if (al->type().istuple()) {
2678+
TupleType* tt = env.getTupleType(al->type());
2679+
for (unsigned int i = 0; i < al->size(); i++) {
2680+
oss << show_with_type(env, (*al)[i], (*tt)[i], showDzn);
2681+
if (i < al->size() - 1) {
2682+
oss << ", ";
2683+
}
2684+
}
2685+
if (al->size() == 1) {
2686+
oss << ",";
2687+
}
2688+
} else {
2689+
// Use element type from al_t since evaluating e may have removed the enum types
2690+
auto elemType = al_t.elemType(env);
2691+
for (unsigned int i = 0; i < al->size(); i++) {
2692+
oss << show_with_type(env, (*al)[i], elemType, showDzn);
2693+
if (i < al->size() - 1) {
2694+
oss << ", ";
2695+
}
2696+
}
2697+
}
2698+
oss << (al->isTuple() ? ")" : "]");
2699+
} else {
2700+
Printer p(oss, 0, false, &env);
2701+
p.print(e);
2702+
}
2703+
return oss.str();
2704+
}
2705+
26152706
} // namespace MiniZinc
26162707

26172708
void debugprint(const MiniZinc::Expression* e) { std::cerr << *e << "\n"; }

lib/stackdump.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
1212

1313
#include <minizinc/ast.hh>
14+
#include <minizinc/builtins.hh>
1415
#include <minizinc/flatten_internal.hh>
1516
#include <minizinc/stackdump.hh>
1617
#include <minizinc/typecheck.hh>
1718

1819
namespace MiniZinc {
19-
StackDump::StackDump(EnvI& env) {
20+
StackDump::StackDump(EnvI& env) : _env(&env) {
2021
// Make sure the call stack items are kept alive
2122
for (auto it = env.callStack.rbegin(); it != env.callStack.rend(); it++) {
2223
bool isCompIter = it->tag;
@@ -87,7 +88,10 @@ void StackDump::print(std::ostream& os) const {
8788
if (isCompIter) {
8889
if ((Expression::cast<Id>(e)->decl()->e() != nullptr) &&
8990
Expression::type(Expression::cast<Id>(e)->decl()->e()).isPar()) {
90-
os << *e << " = " << *Expression::cast<Id>(e)->decl()->e() << std::endl;
91+
os << *e << " = "
92+
<< show_with_type(*_env, Expression::cast<Id>(e)->decl()->e(), Expression::type(e),
93+
false)
94+
<< std::endl;
9195
} else {
9296
os << *e << " = <expression>" << std::endl;
9397
}
@@ -211,7 +215,9 @@ void StackDump::json(std::ostream& os) const {
211215
if (isCompIter) {
212216
if ((Expression::cast<Id>(e)->decl()->e() != nullptr) &&
213217
Expression::type(Expression::cast<Id>(e)->decl()->e()).isPar()) {
214-
ss << *e << " = " << *Expression::cast<Id>(e)->decl()->e();
218+
ss << *e << " = "
219+
<< show_with_type(*_env, Expression::cast<Id>(e)->decl()->e(), Expression::type(e),
220+
false);
215221
} else {
216222
ss << *e << " = <expression>";
217223
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
enum Foo = { A };
2+
constraint forall (x in Foo) (abort("foo"));
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from pathlib import Path
2+
from minizinc import default_driver, Driver
3+
import subprocess
4+
import json
5+
6+
7+
def test_github_912():
8+
here = Path(__file__).resolve().parent
9+
assert isinstance(default_driver, Driver)
10+
model_file = here / "github_912.mzn"
11+
p = subprocess.run(
12+
[default_driver._executable, model_file, "--json-stream"],
13+
stdin=None,
14+
stdout=subprocess.PIPE,
15+
stderr=subprocess.PIPE,
16+
)
17+
assert p.returncode == 1
18+
messages = [json.loads(l) for l in p.stdout.splitlines(False)]
19+
errors = [m for m in messages if m["type"] == "error"]
20+
assert len(errors) == 1
21+
entries = [s for s in errors[0]["stack"] if s["isCompIter"]]
22+
assert len(entries) == 1
23+
assert entries[0]["description"] == "x = A"

0 commit comments

Comments
 (0)