Skip to content
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

Fixed issue with type deduced expressions breaking ternary operators #64

Merged
merged 4 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/utap/typechecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ 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 result_type, type_t thenArg, type_t elseArg) const;
type_t getInlineIfCommonType(type_t t1, type_t t2) const;
bool areEqCompatible(type_t t1, type_t t2) const;
bool isLValue(expression_t) const;
bool isModifiableLValue(expression_t) const;
Expand Down
19 changes: 1 addition & 18 deletions src/ExpressionBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,24 +546,7 @@ void ExpressionBuilder::expr_inline_if()
expression_t e = fragments[0];
fragments.pop(3);

// 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));
fragments.push(expression_t::create_ternary(INLINE_IF, c, t, e, position));
}

void ExpressionBuilder::expr_comma()
Expand Down
19 changes: 17 additions & 2 deletions src/typechecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,20 @@ bool TypeChecker::areInlineIfCompatible(type_t result_type, type_t t1, type_t t2
return areEquivalent(t1, t2);
}

type_t TypeChecker::getInlineIfCommonType(type_t t1, type_t t2) const
{
if (t1.is_record())
return t1;
else if (t2.is_record())
return t2;
else if (TypeChecker::areAssignmentCompatible(t1, t2))
return t1;
else if (TypeChecker::areAssignmentCompatible(t2, t1))
return t2;
else
return type_t{};
}

/**
* Returns true iff \a a and \a b are structurally
* equivalent. However, CONST, SYSTEM_META, and REF are ignored. Scalar sets
Expand Down Expand Up @@ -2083,11 +2097,12 @@ bool TypeChecker::checkExpression(expression_t expr)
handleError(expr, "$First_argument_of_inline_if_must_be_an_integer");
return false;
}
if (!areInlineIfCompatible(expr.get_type(), expr[1].get_type(), expr[2].get_type())) {

type = getInlineIfCommonType(expr[1].get_type(), expr[2].get_type());
if (!areInlineIfCompatible(type, expr[1].get_type(), expr[2].get_type())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to convert the common type to the resulting type

Suggested change
if (!areInlineIfCompatible(type, expr[1].get_type(), expr[2].get_type())) {
if (!areAssignmentCompatible(resultType, type)) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what resultType is supposed to be. But areInlineIfCompatible call does areAssignmentCompatible checks

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given the expression res = (c) ? (a) : (b) then the resultType is the type of res.

handleError(expr, "$Incompatible_arguments_to_inline_if");
return false;
}
type = expr.get_type();
break;

case COMMA:
Expand Down
78 changes: 77 additions & 1 deletion test/test_typechecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,62 @@ TEST_CASE("Ternary operator with arrays clock and double")
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with int and int expression 1")
{
auto doc =
document_fixture{}.add_global_decl("int x; void f(bool b) { x = b ? 0 : 1+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 int and int expression 2")
{
auto doc =
document_fixture{}.add_global_decl("int x; void f(bool b) { x = b ? 1+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 int expressions")
{
auto doc = document_fixture{}
.add_global_decl("int x; void f(bool b) { x = b ? 1+1 : 1+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 int expressions")
{
auto doc = document_fixture{}
.add_global_decl("int x; void f(bool b) { x = b ? 1+1 : 1.0+1.0; }")
.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 int expressions and clocks")
{
auto doc = document_fixture{}
.add_global_decl("int x; clock c; void f(bool b) { x = b ? 1+1 : c; }")
.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 double expressions and clocks")
{
auto doc = document_fixture{}
.add_global_decl("clock x; clock c; void f(bool b) { x = b ? 1+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("Ternary operator with struct clock and double")
{
auto doc = document_fixture{}
Expand All @@ -347,7 +403,7 @@ TEST_CASE("Ternary operator with struct clock and double")
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Terneray operator returning c++ reference to doubles with assignment")
TEST_CASE("Ternary 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; }")
Expand All @@ -357,6 +413,26 @@ TEST_CASE("Terneray operator returning c++ reference to doubles with assignment"
CHECK_MESSAGE(doc->get_warnings().size() == 0, doc->get_warnings()[0].msg);
}

TEST_CASE("Ternary operator with two conversions into clock")
{
auto doc = document_fixture{}
.add_global_decl("clock c; double x; void f(bool b) { c = b ? 1 : x+2.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 two conversions into double")
{
auto doc = document_fixture{}
.add_global_decl("clock c; double x; void f(bool b) { x = b ? 1 : c+2.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("Double in struct")
{
auto doc = document_fixture{}.add_default_process().add_global_decl("struct { double x; } my_struct;").parse();
Expand Down