Skip to content

Commit

Permalink
Merge pull request #61 from thorulf4/ternery_operator
Browse files Browse the repository at this point in the history
Allows implicit clock to double conversions in ternary conditional operators during type checking
  • Loading branch information
thorulf4 authored Sep 15, 2023
2 parents b3a2909 + b3c6954 commit 1b59317
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 13 deletions.
4 changes: 2 additions & 2 deletions include/utap/typechecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,8 @@ class TypeChecker : public DocumentVisitor, public AbstractStatementVisitor

expression_t checkInitialiser(type_t type, expression_t init);
bool areAssignmentCompatible(type_t lvalue, type_t rvalue, bool init = false) const;
bool areInlineIfCompatible(type_t thenArg, type_t elseArg) const;
bool areInlineIfCompatible(type_t result_type, type_t thenArg, type_t elseArg) const;
bool areEqCompatible(type_t t1, type_t t2) const;
bool areEquivalent(type_t, type_t) const;
bool isLValue(expression_t) const;
bool isModifiableLValue(expression_t) const;
bool isUniqueReference(expression_t expr) const;
Expand All @@ -86,6 +85,7 @@ class TypeChecker : public DocumentVisitor, public AbstractStatementVisitor
void checkType(type_t, bool initialisable = false, bool inStruct = false);

public:
static bool areEquivalent(type_t, type_t);
explicit TypeChecker(Document& doc, bool refinement = false);
void visitTemplateAfter(template_t&) override;
bool visitTemplateBefore(template_t&) override;
Expand Down
24 changes: 22 additions & 2 deletions src/ExpressionBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#include "utap/ExpressionBuilder.hpp"

#include "utap/typechecker.h"

#include <sstream>
#include <string>
#include <vector>
Expand Down Expand Up @@ -460,7 +462,7 @@ void ExpressionBuilder::expr_unary(kind_t unaryop) // 1 expr
case MINUS:
unaryop = UNARY_MINUS;
/* Fall through! */
default: fragments[0] = expression_t::create_unary(unaryop, fragments[0], position);
default: fragments[0] = expression_t::create_unary(unaryop, fragments[0], position, fragments[0].get_type());
}
}

Expand Down Expand Up @@ -543,7 +545,25 @@ void ExpressionBuilder::expr_inline_if()
expression_t t = fragments[1];
expression_t e = fragments[0];
fragments.pop(3);
fragments.push(expression_t::create_ternary(INLINE_IF, c, t, e, position, t.get_type()));

// Find the common type of expression t and e
type_t t1 = t.get_type();
type_t t2 = e.get_type();
type_t common_type;
if (t1.is_record())
common_type = t1;
else if (t2.is_record())
common_type = t2;
else if (t1.is_clock() && !t2.is_clock() || !t1.is_clock() && t2.is_clock())
common_type = type_t{DOUBLE, {}, 0};
else if (TypeChecker::areEquivalent(t1, t2))
common_type = t1;
else
handle_error(TypeException{"$Incompatible_arguments_to_inline_if"});

type_t type = t.get_type().is_clock() && e.get_type().is_double() ? e.get_type() : t.get_type();

fragments.push(expression_t::create_ternary(INLINE_IF, c, t, e, position, common_type));
}

void ExpressionBuilder::expr_comma()
Expand Down
15 changes: 7 additions & 8 deletions src/typechecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1515,21 +1515,20 @@ expression_t TypeChecker::checkInitialiser(type_t type, expression_t init)
same size and the subtypes must be compatible. In case of records,
they must have the same type name.
*/
bool TypeChecker::areInlineIfCompatible(type_t t1, type_t t2) const
bool TypeChecker::areInlineIfCompatible(type_t result_type, type_t t1, type_t t2) const
{
if (t1.is_integral() && t2.is_integral()) {
if (areAssignmentCompatible(result_type, t1) && areAssignmentCompatible(result_type, t1))
return true;
} else {
return areEquivalent(t1, t2);
}

return areEquivalent(t1, t2);
}

/**
* Returns true iff \a a and \a b are structurally
* equivalent. However, CONST, SYSTEM_META, and REF are ignored. Scalar sets
* are checked using named equivalence.
*/
bool TypeChecker::areEquivalent(type_t a, type_t b) const
bool TypeChecker::areEquivalent(type_t a, type_t b)
{
if (a.is_integer() && b.is_integer()) {
return !a.is(RANGE) || !b.is(RANGE) ||
Expand Down Expand Up @@ -2084,11 +2083,11 @@ bool TypeChecker::checkExpression(expression_t expr)
handleError(expr, "$First_argument_of_inline_if_must_be_an_integer");
return false;
}
if (!areInlineIfCompatible(expr[1].get_type(), expr[2].get_type())) {
if (!areInlineIfCompatible(expr.get_type(), expr[1].get_type(), expr[2].get_type())) {
handleError(expr, "$Incompatible_arguments_to_inline_if");
return false;
}
type = expr[1].get_type();
type = expr.get_type();
break;

case COMMA:
Expand Down
161 changes: 160 additions & 1 deletion test/test_typechecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ TEST_SUITE("Quantifier forall")
df.add_system_decl("bool b[3] = {1,1,1};");
df.add_system_decl("bool x = forall(i : int[0,2]) b[i];");
auto doc = df.add_default_process().parse();
CHECK(doc->get_warnings().size() == 0);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
const auto errors = doc->get_errors();
REQUIRE(errors.size() == 1);
CHECK(errors[0].msg == "$Must_be_computable_at_compile_time");
Expand Down Expand Up @@ -198,6 +198,165 @@ TEST_SUITE("Error positions for unbound parameters")
}
}

TEST_CASE("Ternary operator with clock and double")
{
auto doc = document_fixture{}
.add_global_decl("clock c; double x; void f(bool b) { x = b ? c : 1.0; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with double and clock")
{
auto doc = document_fixture{}
.add_global_decl("clock c; double x; void f(bool b) { x = b ? 1.0 : c; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with clock and integer")
{
auto doc = document_fixture{}
.add_global_decl("clock c; double x; void f(bool b) { x = b ? c : 1; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with clock and bool")
{
auto doc = document_fixture{}
.add_global_decl("clock c; double x; void f(bool b) { x = b ? c : true; };")
.add_default_process()
.parse();
CHECK(doc->get_errors().size() == 1);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with clock and clock")
{
auto doc = document_fixture{}
.add_global_decl("clock c; double x; void f(bool b) { x = b ? c : c; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with constant double")
{
auto doc = document_fixture{}
.add_global_decl("const double VAL = 2;")
.add_global_decl("double x; void f(bool b) { x = b ? -VAL : VAL; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with constant double and clock")
{
auto doc = document_fixture{}
.add_global_decl("const double VAL = 2;")
.add_global_decl("clock c;")
.add_global_decl("double x; void f(bool b) { x = b ? -VAL : c; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with boolean and clock")
{
auto doc = document_fixture{}
.add_global_decl("clock c;")
.add_global_decl("double x; void f(bool b) { x = b? true : c; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with struct and double")
{
auto doc = document_fixture{}
.add_global_decl("struct { int x; } s;")
.add_global_decl("double x; void f(bool b) { x = b? s : 0.5; }")
.add_default_process()
.parse();
CHECK(doc->get_errors().size() == 1);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with struct and double")
{
auto doc = document_fixture{}
.add_global_decl("struct { int x; } s;")
.add_global_decl("double x; void f(bool b) { x = b? s : 0.5; }")
.add_default_process()
.parse();
CHECK(doc->get_errors().size() == 1);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with struct and struct")
{
auto doc = document_fixture{}
.add_global_decl("typedef struct { int x; } S;")
.add_global_decl("S s; S x = {5}; S y = {2};")
.add_global_decl("void f(bool b) { s = b? x : y; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with reference to integer array")
{
auto doc = document_fixture{}
.add_global_decl("int x[2]; int y[2]; int z[2];")
.add_global_decl("void f(bool b) { z = (b?x:y); }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with arrays clock and double")
{
auto doc = document_fixture{}
.add_global_decl("clock c; double x[2]; void f(bool b) { x[0] = b ? c : x[1]; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with struct clock and double")
{
auto doc = document_fixture{}
.add_global_decl("struct{ clock c; double x; }z; void f(bool b) { z.x = b ? z.c : 1.0; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Terneray operator returning c++ reference to doubles with assignment")
{
auto doc = document_fixture{}
.add_global_decl("clock c; double x[2]; void f(bool b) { (b?x[0]:x[1]) = c; }")
.add_default_process()
.parse();
CHECK_MESSAGE(doc->get_errors().size() == 0, doc->get_errors()[0].msg);
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Double in struct")
{
auto doc = document_fixture{}.add_default_process().add_global_decl("struct { double x; } my_struct;").parse();
Expand Down

0 comments on commit 1b59317

Please sign in to comment.