Fix casting with conditional expressions

The following scenario would fail compiling to C++ because we failed to
determine the return type of the conditional expression:

    Test := Rectangle {
        property<bool> condition;
        property<color> extra_color;
        color: condition ? root.extra_color : 4289374890;
    }

The type of the true branch would be color and the false branch would be
a float. Since they "disagree", ty() on the expression would return
Type::Invalid. This was temporarily worked around in the C++ generator
by always returning the type of the true branch, but that's wrong.

Instead this patch changes maybe_convert_to to apply the Cast expression
to the individual branches, placing the cast only to the numberic
literal and correcting the return value of ty() on the conditional
expression.
This commit is contained in:
Simon Hausmann 2020-06-11 13:38:24 +02:00
parent 0d4f370e95
commit 03bef6dba3
4 changed files with 52 additions and 18 deletions

View file

@ -1,5 +1,5 @@
use crate::object_tree::*;
use crate::{parser::SyntaxNode, typeregister::Type};
use crate::{diagnostics::Diagnostics, parser::Spanned, parser::SyntaxNode, typeregister::Type};
use core::cell::RefCell;
use std::rc::Weak;
@ -167,4 +167,48 @@ impl Expression {
Expression::Condition { .. } => false,
}
}
pub fn cast_boxed(self: Box<Self>, target_type: Type) -> Box<Expression> {
Box::new(Expression::Cast { from: self, to: target_type })
}
/// Create a conversion node if needed, or throw an error if the type is not matching
pub fn maybe_convert_to(
self,
target_type: Type,
node: &SyntaxNode,
diag: &mut Diagnostics,
) -> Expression {
match self {
Expression::Condition { condition, true_expr, false_expr } => {
let true_expr = if true_expr.ty() != target_type {
true_expr.cast_boxed(target_type.clone())
} else {
true_expr
};
let false_expr = if false_expr.ty() != target_type {
false_expr.cast_boxed(target_type)
} else {
false_expr
};
Expression::Condition { condition, true_expr, false_expr }
}
_ => {
let ty = self.ty();
if ty == target_type {
self
} else if ty.can_convert(&target_type) {
Expression::Cast { from: Box::new(self), to: target_type }
} else if ty == Type::Invalid {
self
} else {
diag.push_error(
format!("Cannot convert {} to {}", ty, target_type),
node.span(),
);
self
}
}
}
}
}

View file

@ -377,7 +377,7 @@ fn compile_expression(e: &crate::expression_tree::Expression) -> String {
let false_code = compile_expression(false_expr);
format!(
r#"[&]() -> {} {{ if ({}) {{ return {}; }} else {{ return {}; }}}}()"#,
true_expr.ty().cpp_type().unwrap(),
e.ty().cpp_type().unwrap(),
cond_code,
true_code,
false_code

View file

@ -82,7 +82,7 @@ impl Expression {
node.child_node(SyntaxKind::CodeBlock).map(|c| Self::from_codeblock_node(c, ctx))
})
.unwrap_or(Self::Invalid);
maybe_convert_to(e, ctx, &node)
e.maybe_convert_to(ctx.property_type.clone(), &node, &mut ctx.diag)
}
fn from_codeblock_node(node: SyntaxNode, ctx: &mut LookupCtx) -> Expression {
@ -349,21 +349,6 @@ impl Expression {
}
}
/// Create a conversion node if needed, or throw an error if the type is not matching
fn maybe_convert_to(e: Expression, ctx: &mut LookupCtx, node: &SyntaxNode) -> Expression {
let ty = e.ty();
if ty == ctx.property_type {
e
} else if ty.can_convert(&ctx.property_type) {
Expression::Cast { from: Box::new(e), to: ctx.property_type.clone() }
} else if ty == Type::Invalid {
e
} else {
ctx.diag.push_error(format!("Cannot convert {} to {}", ty, ctx.property_type), node.span());
e
}
}
fn unescape_string(string: &str) -> Option<String> {
if !string.starts_with('"') || !string.ends_with('"') {
return None;

View file

@ -0,0 +1,5 @@
Test := Rectangle {
property<bool> condition;
property<color> extra_color;
color: condition ? root.extra_color : 4289374890;
}