Skip to content

Commit

Permalink
refs #3317 fixed inconsistencies handling push and unshift with lvars…
Browse files Browse the repository at this point in the history
… with complex types
  • Loading branch information
davidnich committed Mar 5, 2019
1 parent dc5256f commit a8db56a
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 24 deletions.
5 changes: 4 additions & 1 deletion doxygen/lang/900_release_notes.dox.tmpl
Expand Up @@ -17,9 +17,12 @@
@subsection qore_091_bug_fixes Bug Fixes in Qore
- fixed a bug where an invalid parse exception would be raised with an assignment of a global variable to another
global variable with the same name in a different namespace
(<a href="https://github.com/qorelanguage/qore/issues/3318">issue 3318</a>)
(<a href="https://github.com/qorelanguage/qore/issues/3337">issue 3337</a>)
- implemented support for serializing and deserializing complex type information in lists and hashes
(<a href="https://github.com/qorelanguage/qore/issues/3318">issue 3318</a>)
- fixed bugs handling unassigned lvalues with complex type declarations with the @ref push and @ref unshift
operators
(<a href="https://github.com/qorelanguage/qore/issues/3317">issue 3317</a>)
- fixed a memory leak in copy onstructor execution in complex class hierarchies
(<a href="https://github.com/qorelanguage/qore/issues/3312">issue 3312</a>)
- implemented support for automatically initializing the \c %Qore library from Java when dynamically loaded from
Expand Down
146 changes: 137 additions & 9 deletions examples/test/qore/operators/list_ops.qtest
Expand Up @@ -73,30 +73,94 @@ class Test inherits QUnit::Test {
*list l2;
softlist l3;
*softlist l4;
list<int> l5;
*list<int> l6;
softlist<int> l7;
*softlist<int> l8;
list<softint> l9;
*list<softint> l10;
softlist<softint> l11;
*softlist<softint> l12;

assertEq((3,), push l1, 3);
assertThrows("PUSH-ERROR", sub() { push l2, 3; });
assertEq((3,), push l2, 3);
assertEq((3,), push l3, 3);
assertThrows("PUSH-ERROR", sub() { push l4, 3; });
assertEq((3,), push l4, 3);
assertEq((3,), push l5, 3);
assertEq((3,), push l6, 3);
assertEq((3,), push l7, 3);
assertEq((3,), push l8, 3);
assertEq((3,), push l9, "3");
assertEq((3,), push l10, "3");
assertEq((3,), push l11, "3");
assertEq((3,), push l12, "3");
assertEq("list", l1.fullType());
assertEq("list", l2.fullType());
assertEq("list", l3.fullType());
assertEq("list", l4.fullType());
assertEq("list<int>", l5.fullType());
assertEq("list<int>", l6.fullType());
assertEq("list<int>", l7.fullType());
assertEq("list<int>", l8.fullType());
assertEq("list<softint>", l9.fullType());
assertEq("list<softint>", l10.fullType());
assertEq("list<softint>", l11.fullType());
assertEq("list<softint>", l12.fullType());
}

pushElementAfterDelete() {
list l1 = (1, 2);
*list l2 = (1, 2);
softlist l3 = (1, 2);
*softlist l4 = (1, 2);
list<int> l5 = (1, 2);
*list<int> l6 = (1, 2);
softlist<int> l7 = (1, 2);
*softlist<int> l8 = (1, 2);
list<softint> l9 = ("1", 2);
*list<softint> l10 = ("1", 2);
softlist<softint> l11 = ("1", 2);
*softlist<softint> l12 = ("1", 2);

delete l1;
delete l2;
delete l3;
delete l4;
delete l5;
delete l6;
delete l7;
delete l8;
delete l9;
delete l10;
delete l11;
delete l12;

assertEq((3,), push l1, 3);
assertThrows("PUSH-ERROR", sub() { push l2, 3; });
assertEq((3,), push l2, 3);
assertEq((3,), push l3, 3);
assertThrows("PUSH-ERROR", sub() { push l4, 3; });
assertEq((3,), push l4, 3);
assertEq((3,), push l5, 3);
assertEq((3,), push l6, 3);
assertEq((3,), push l7, 3);
assertEq((3,), push l8, 3);
assertEq((3,), push l9, "3");
assertEq((3,), push l10, "3");
assertEq((3,), push l11, "3");
assertEq((3,), push l12, "3");
assertEq("list", l1.fullType());
assertEq("list", l2.fullType());
assertEq("list", l3.fullType());
assertEq("list", l4.fullType());
assertEq("list<int>", l5.fullType());
assertEq("list<int>", l6.fullType());
assertEq("list<int>", l7.fullType());
assertEq("list<int>", l8.fullType());
assertEq("list<softint>", l9.fullType());
assertEq("list<softint>", l10.fullType());
assertEq("list<softint>", l11.fullType());
assertEq("list<softint>", l12.fullType());
}


unshiftElement() {
list l = (1, 2);
assertEq((3, 1, 2), unshift l, 3);
Expand Down Expand Up @@ -132,27 +196,91 @@ class Test inherits QUnit::Test {
*list l2;
softlist l3;
*softlist l4;
list<int> l5;
*list<int> l6;
softlist<int> l7;
*softlist<int> l8;
list<softint> l9;
*list<softint> l10;
softlist<softint> l11;
*softlist<softint> l12;
assertEq((3,), unshift l1, 3);
assertThrows("UNSHIFT-ERROR", sub() { unshift l2, 3; });
assertEq((3,), unshift l2, 3);
assertEq((3,), unshift l3, 3);
assertThrows("UNSHIFT-ERROR", sub() { unshift l4, 3; });
assertEq((3,), unshift l4, 3);
assertEq((3,), unshift l5, 3);
assertEq((3,), unshift l6, 3);
assertEq((3,), unshift l7, 3);
assertEq((3,), unshift l8, 3);
assertEq((3,), unshift l9, "3");
assertEq((3,), unshift l10, "3");
assertEq((3,), unshift l11, "3");
assertEq((3,), unshift l12, "3");
assertEq("list", l1.fullType());
assertEq("list", l2.fullType());
assertEq("list", l3.fullType());
assertEq("list", l4.fullType());
assertEq("list<int>", l5.fullType());
assertEq("list<int>", l6.fullType());
assertEq("list<int>", l7.fullType());
assertEq("list<int>", l8.fullType());
assertEq("list<softint>", l9.fullType());
assertEq("list<softint>", l10.fullType());
assertEq("list<softint>", l11.fullType());
assertEq("list<softint>", l12.fullType());
}

unshiftElementAfterDelete() {
list l1 = (1, 2);
*list l2 = (1, 2);
softlist l3 = (1, 2);
*softlist l4 = (1, 2);
list<int> l5 = (1, 2);
*list<int> l6 = (1, 2);
softlist<int> l7 = (1, 2);
*softlist<int> l8 = (1, 2);
list<softint> l9 = ("1", 2);
*list<softint> l10 = ("1", 2);
softlist<softint> l11 = ("1", 2);
*softlist<softint> l12 = ("1", 2);

delete l1;
delete l2;
delete l3;
delete l4;
delete l5;
delete l6;
delete l7;
delete l8;
delete l9;
delete l10;
delete l11;
delete l12;

assertEq((3,), unshift l1, 3);
assertThrows("UNSHIFT-ERROR", sub() { unshift l2, 3; });
assertEq((3,), unshift l2, 3);
assertEq((3,), unshift l3, 3);
assertThrows("UNSHIFT-ERROR", sub() { unshift l4, 3; });
assertEq((3,), unshift l4, 3);
assertEq((3,), unshift l5, 3);
assertEq((3,), unshift l6, 3);
assertEq((3,), unshift l7, 3);
assertEq((3,), unshift l8, 3);
assertEq((3,), unshift l9, "3");
assertEq((3,), unshift l10, "3");
assertEq((3,), unshift l11, "3");
assertEq((3,), unshift l12, "3");
assertEq("list", l1.fullType());
assertEq("list", l2.fullType());
assertEq("list", l3.fullType());
assertEq("list", l4.fullType());
assertEq("list<int>", l5.fullType());
assertEq("list<int>", l6.fullType());
assertEq("list<int>", l7.fullType());
assertEq("list<int>", l8.fullType());
assertEq("list<softint>", l9.fullType());
assertEq("list<softint>", l10.fullType());
assertEq("list<softint>", l11.fullType());
assertEq("list<softint>", l12.fullType());
}

popBasics() {
Expand Down
73 changes: 70 additions & 3 deletions examples/test/qore/operators/operators.qtest
Expand Up @@ -39,6 +39,7 @@ class DataTest {

class Test inherits QUnit::Test {
constructor() : QUnit::Test("operators", "1.0", \ARGV) {
addTestCase("push unshift tests", \pushUnshiftTests());
addTestCase("basic operator tests", \basicTests());
addTestCase("date tests", \dateTests());
addTestCase("lvalue tests", \lvalueTests());
Expand Down Expand Up @@ -66,6 +67,42 @@ class Test inherits QUnit::Test {
set_return_value(main());
}

pushUnshiftTests() {
# issue #3317: ensure typed and untyped lists are handled equally with push and unshift
{
list l;
push l, 1;
assertEq((1,), l);
remove l;
unshift l, 1;
assertEq((1,), l);
}
{
list<int> l;
push l, 1;
assertEq((1,), l);
remove l;
unshift l, 1;
assertEq((1,), l);
}
{
*list l;
push l, 1;
assertEq((1,), l);
remove l;
unshift l, 1;
assertEq((1,), l);
}
{
*list<int> l;
push l, 1;
assertEq((1,), l);
remove l;
unshift l, 1;
assertEq((1,), l);
}
}

pushPlusEqualsTests() {
{
list l0;
Expand All @@ -87,21 +124,51 @@ class Test inherits QUnit::Test {
*list l0;
*list l1;
assertThrows("RUNTIME-TYPE-ERROR", sub () { l0 += 1; });
assertThrows("PUSH-ERROR", sub () { push l1, 1; });
# issue #3317: push always operates on lists, so if the lvalue is not assigned and can accept a list, then
# a list is created
push l1, 1;
assertEq((1,), l1);
}
{
*list l1;
# issue #3317: unshift always operates on lists, so if the lvalue is not assigned and can accept a list,
# then a list is created
unshift l1, 1;
assertEq((1,), l1);
}
{
*softlist l0;
*softlist l1;
l0 += 1;
assertEq((1,), l0);
assertThrows("PUSH-ERROR", sub () { push l1, 1; });
# issue #3317: push always operates on lists, so if the lvalue is not assigned and can accept a list, then
# a list is created
push l1, 1;
assertEq((1,), l1);
}
{
*softlist l1;
# issue #3317: unshift always operates on lists, so if the lvalue is not assigned and can accept a list,
# then a list is created
unshift l1, 1;
assertEq((1,), l1);
}
{
auto l0;
auto l1;
l0 += 1;
assertEq(1, l0);
assertThrows("PUSH-ERROR", sub () { push l1, 1; });
# issue #3317: push always operates on lists, so if the lvalue is not assigned and can accept a list, then
# a list is created
push l1, 1;
assertEq((1,), l1);
}
{
auto l1;
# issue #3317: unshift always operates on lists, so if the lvalue is not assigned and can accept a list,
# then a list is created
unshift l1, 1;
assertEq((1,), l1);
}
{
int l0;
Expand Down
2 changes: 1 addition & 1 deletion lib/QorePlusEqualsOperatorNode.cpp
Expand Up @@ -3,7 +3,7 @@
Qore Programming Language
Copyright (C) 2003 - 2018 Qore Technologies, s.r.o.
Copyright (C) 2003 - 2019 Qore Technologies, s.r.o.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
Expand Down
15 changes: 10 additions & 5 deletions lib/QorePushOperatorNode.cpp
Expand Up @@ -3,7 +3,7 @@
Qore Programming Language
Copyright (C) 2003 - 2018 Qore Technologies, s.r.o.
Copyright (C) 2003 - 2019 Qore Technologies, s.r.o.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
Expand Down Expand Up @@ -44,10 +44,15 @@ QoreValue QorePushOperatorNode::evalImpl(bool& needs_deref, ExceptionSink* xsink

// assign to a blank list if the lvalue has no value yet but is typed as a list or a softlist
if (val.getType() == NT_NOTHING) {
if (val.getTypeInfo() == listTypeInfo && val.assign(QoreTypeInfo::getDefaultQoreValue(listTypeInfo)))
return QoreValue();
if (val.getTypeInfo() == softListTypeInfo && val.assign(QoreTypeInfo::getDefaultQoreValue(softListTypeInfo)))
return QoreValue();
const QoreTypeInfo* vti = val.getTypeInfo();
if (QoreTypeInfo::parseAcceptsReturns(vti, NT_LIST)) {
// issue #3317: if the lvar can be a list, assign the current runtime type based on the declared complex list type
const QoreTypeInfo* lti = vti == autoTypeInfo ? autoTypeInfo : QoreTypeInfo::getReturnComplexListOrNothing(vti);
if (val.assign(new QoreListNode(lti))) {
assert(*xsink);
return QoreValue();
}
}
}

// value is not a list, so throw exception
Expand Down
15 changes: 10 additions & 5 deletions lib/QoreUnshiftOperatorNode.cpp
Expand Up @@ -3,7 +3,7 @@
Qore Programming Language
Copyright (C) 2003 - 2018 Qore Technologies, s.r.o.
Copyright (C) 2003 - 2019 Qore Technologies, s.r.o.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
Expand Down Expand Up @@ -72,10 +72,15 @@ QoreValue QoreUnshiftOperatorNode::evalImpl(bool& needs_deref, ExceptionSink* xs

// assign to a blank list if the lvalue has no value yet but is typed as a list or a softlist
if (val.getType() == NT_NOTHING) {
if (val.getTypeInfo() == listTypeInfo && val.assign(QoreTypeInfo::getDefaultQoreValue(listTypeInfo)))
return QoreValue();
if (val.getTypeInfo() == softListTypeInfo && val.assign(QoreTypeInfo::getDefaultQoreValue(softListTypeInfo)))
return QoreValue();
const QoreTypeInfo* vti = val.getTypeInfo();
if (QoreTypeInfo::parseAcceptsReturns(vti, NT_LIST)) {
// issue #3317: if the lvar can be a list, assign the current runtime type based on the declared complex list type
const QoreTypeInfo* lti = vti == autoTypeInfo ? autoTypeInfo : QoreTypeInfo::getReturnComplexListOrNothing(vti);
if (val.assign(new QoreListNode(lti))) {
assert(*xsink);
return QoreValue();
}
}
}

// value is not a list, so throw exception
Expand Down

0 comments on commit a8db56a

Please sign in to comment.