Skip to content

Commit

Permalink
TASK: Cleanup InferredTypes and extract duplicated logic to TypeInfer…
Browse files Browse the repository at this point in the history
…rerContext
  • Loading branch information
mhsdesign committed Apr 23, 2023
1 parent 22df4ba commit 0fef930
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 27 deletions.
23 changes: 20 additions & 3 deletions src/TypeSystem/Inferrer/InferredTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,30 @@
class InferredTypes
{
/**
* @var TypeInterface[]
* Map of identifierName to the corresponding inferred type
* @var array<string,TypeInterface>
*/
public readonly array $types;
private readonly array $types;

public function __construct(
private function __construct(
TypeInterface ...$types
) {
// @phpstan-ignore-next-line
$this->types = $types;
}

public static function empty(): self
{
return new self();
}

public static function fromType(string $identifierName, TypeInterface $type): self
{
return new self(...[$identifierName => $type]);
}

public function getType(string $identifierName): ?TypeInterface
{
return $this->types[$identifierName] ?? null;
}
}
30 changes: 11 additions & 19 deletions src/TypeSystem/Inferrer/TypeInferrer.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,19 @@ public function inferTypesInCondition(ExpressionNode $conditionNode, TypeInferre
{
if ($conditionNode->root instanceof IdentifierNode) {
$type = $this->scope->lookupTypeFor($conditionNode->root->value);
// case `nullableString ? "nullableString is not null" : "nullableString is null"`
if (!$type instanceof UnionType || !$type->containsNull()) {
return new InferredTypes();
if (!$type) {
return InferredTypes::empty();
}

return new InferredTypes(
...[$conditionNode->root->value => $context->isTrue() ? $type->withoutNull() : NullType::get()]
);
// case `nullableString ? "nullableString is not null" : "nullableString is null"`
return InferredTypes::fromType($conditionNode->root->value, $context->narrowDownType($type));
}

if (($binaryOperationNode = $conditionNode->root) instanceof BinaryOperationNode) {
// cases
// `nullableString === null ? "nullableString is null" : "nullableString is not null"`
// `nullableString !== null ? "nullableString is not null" : "nullableString is null"`
if (count($binaryOperationNode->operands->rest) !== 1) {
return new InferredTypes();
return InferredTypes::empty();
}
$first = $binaryOperationNode->operands->first;
$second = $binaryOperationNode->operands->rest[0];
Expand All @@ -82,26 +79,21 @@ public function inferTypesInCondition(ExpressionNode $conditionNode, TypeInferre
};

if ($comparedIdentifierValueToNull === null) {
return new InferredTypes();
return InferredTypes::empty();
}

$type = $this->scope->lookupTypeFor($comparedIdentifierValueToNull);
if (!$type instanceof UnionType || !$type->containsNull()) {
return new InferredTypes();
if (!$type) {
return InferredTypes::empty();
}

if ($binaryOperationNode->operator === BinaryOperator::EQUAL) {
return new InferredTypes(
...[$comparedIdentifierValueToNull => $context->isTrue() ? NullType::get() : $type->withoutNull()]
);
return InferredTypes::fromType($comparedIdentifierValueToNull, $context->negate()->narrowDownType($type));
}
if ($binaryOperationNode->operator === BinaryOperator::NOT_EQUAL) {
return new InferredTypes(
...[$comparedIdentifierValueToNull => $context->isTrue() ? $type->withoutNull() : NullType::get()]
);
return InferredTypes::fromType($comparedIdentifierValueToNull, $context->narrowDownType($type));
}
}

return new InferredTypes();
return InferredTypes::empty();
}
}
21 changes: 17 additions & 4 deletions src/TypeSystem/Inferrer/TypeInferrerContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,32 @@

namespace PackageFactory\ComponentEngine\TypeSystem\Inferrer;

use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

enum TypeInferrerContext
{
case TRUTHY;

case FALSY;

public function isTrue(): bool
public function negate(): self
{
return $this === self::TRUTHY;
return match ($this) {
self::TRUTHY => self::FALSY,
self::FALSY => self::TRUTHY
};
}

public function isFalse(): bool
public function narrowDownType(TypeInterface $type): TypeInterface
{
return $this === self::FALSY;
if (!$type instanceof UnionType || !$type->containsNull()) {
return $type;
}
return match ($this) {
self::TRUTHY => $type->withoutNull(),
self::FALSY => NullType::get()
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static function forFalsyBranch(ExpressionNode $conditionNode, ScopeInterf

public function lookupTypeFor(string $name): ?TypeInterface
{
return $this->inferredTypes->types[$name] ?? $this->parentScope->lookupTypeFor($name);
return $this->inferredTypes->getType($name) ?? $this->parentScope->lookupTypeFor($name);
}

public function resolveTypeReference(TypeReferenceNode $typeReferenceNode): TypeInterface
Expand Down

0 comments on commit 0fef930

Please sign in to comment.