Skip to content

Commit 1b476e5

Browse files
committed
TASK: Type inference for null comparison in ternary
1 parent 8a34ee1 commit 1b476e5

3 files changed

Lines changed: 44 additions & 7 deletions

File tree

src/TypeSystem/Resolver/TernaryOperation/TernaryOperationTypeResolver.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public function resolveTypeOf(TernaryOperationNode $ternaryOperationNode): TypeI
4444
);
4545
$conditionNode = $ternaryOperationNode->condition;
4646

47-
$conditionType = $expressionTypeResolver->resolveTypeOf($conditionNode);
47+
// @todo for eager type checks?
48+
$expressionTypeResolver->resolveTypeOf($conditionNode);
4849

4950
if ($conditionNode->root instanceof BooleanLiteralNode) {
5051
return $conditionNode->root->value
@@ -55,7 +56,6 @@ public function resolveTypeOf(TernaryOperationNode $ternaryOperationNode): TypeI
5556
$trueExpressionTypeResolver = new ExpressionTypeResolver(
5657
scope: new TernaryBranchScope(
5758
$ternaryOperationNode->condition,
58-
$conditionType,
5959
true,
6060
$this->scope
6161
)
@@ -64,7 +64,6 @@ public function resolveTypeOf(TernaryOperationNode $ternaryOperationNode): TypeI
6464
$falseExpressionTypeResolver = new ExpressionTypeResolver(
6565
scope: new TernaryBranchScope(
6666
$ternaryOperationNode->condition,
67-
$conditionType,
6867
false,
6968
$this->scope
7069
)

src/TypeSystem/Scope/ShallowScope/TernaryBranchScope.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222

2323
namespace PackageFactory\ComponentEngine\TypeSystem\Scope\ShallowScope;
2424

25+
use PackageFactory\ComponentEngine\Definition\BinaryOperator;
26+
use PackageFactory\ComponentEngine\Parser\Ast\BinaryOperationNode;
2527
use PackageFactory\ComponentEngine\Parser\Ast\ExpressionNode;
2628
use PackageFactory\ComponentEngine\Parser\Ast\IdentifierNode;
29+
use PackageFactory\ComponentEngine\Parser\Ast\NullLiteralNode;
2730
use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
2831
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
2932
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
@@ -34,21 +37,42 @@ final class TernaryBranchScope implements ScopeInterface
3437
{
3538
public function __construct(
3639
private readonly ExpressionNode $conditionNode,
37-
private readonly TypeInterface $conditionType,
3840
private readonly bool $isBranchLeft,
3941
private readonly ScopeInterface $parentScope
4042
) {
4143
}
4244

4345
public function lookupTypeFor(string $name): ?TypeInterface
4446
{
47+
$type = $this->parentScope->lookupTypeFor($name);
48+
49+
if (!$type instanceof UnionType || !$type->containsNull()) {
50+
return $type;
51+
}
52+
4553
if ($this->conditionNode->root instanceof IdentifierNode && $this->conditionNode->root->value === $name) {
46-
if ($this->conditionType instanceof UnionType && $this->conditionType->containsNull()) {
47-
return $this->isBranchLeft ? $this->conditionType->withoutNull() : NullType::get();
54+
return $this->isBranchLeft ? $type->withoutNull() : NullType::get();
55+
}
56+
57+
if (($binaryOperationNode = $this->conditionNode->root) instanceof BinaryOperationNode) {
58+
foreach ($binaryOperationNode->operands as $operand) {
59+
if (!$operand->root instanceof NullLiteralNode
60+
&& !($operand->root instanceof IdentifierNode && $operand->root->value === $name)
61+
) {
62+
return $type;
63+
}
64+
}
65+
66+
if ($binaryOperationNode->operator === BinaryOperator::EQUAL) {
67+
return $this->isBranchLeft ? NullType::get() : $type->withoutNull();
68+
}
69+
70+
if ($binaryOperationNode->operator === BinaryOperator::NOT_EQUAL) {
71+
return $this->isBranchLeft ? $type->withoutNull() : NullType::get();
4872
}
4973
}
5074

51-
return $this->parentScope->lookupTypeFor($name);
75+
return $type;
5276
}
5377

5478
public function resolveTypeReference(TypeReferenceNode $typeReferenceNode): TypeInterface

test/Unit/TypeSystem/Resolver/TernaryOperation/TernaryOperationTypeResolverTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ public function ternaryOperationExamples(): array
5151
'nullableString ? nullableString : "fallback"' => [
5252
'nullableString ? nullableString : "fallback"', StringType::get()
5353
],
54+
'nullableString === null ? "" : nullableString' => [
55+
'nullableString === null ? "" : nullableString', StringType::get()
56+
],
57+
// Tue es oder tue es nicht. Es gibt kein Versuchen.
58+
'null === nullableString ? "" : nullableString' => [
59+
'null === nullableString ? "" : nullableString', StringType::get()
60+
],
61+
'nullableString !== null ? nullableString : ""' => [
62+
'nullableString !== null ? nullableString : ""', StringType::get()
63+
],
64+
// Patience you must have my young Padawan.
65+
'null !== nullableString ? nullableString : ""' => [
66+
'null !== nullableString ? nullableString : ""', StringType::get()
67+
],
5468
'nullableString ? null : nullableString' => [
5569
'nullableString ? null : nullableString', NullType::get()
5670
],

0 commit comments

Comments
 (0)