Skip to content

Commit

Permalink
Merge pull request #2288 from zephir-lang/#2276-mixed-type
Browse files Browse the repository at this point in the history
#2276 - Add support for `mixed` type
  • Loading branch information
Jeckerson authored Oct 5, 2021
2 parents 4ef01e4 + 477bc37 commit a41cb40
Show file tree
Hide file tree
Showing 20 changed files with 742 additions and 117 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org).
## [Unreleased]
# Added
- Added support for `string` type in php.ini [#2280](https://github.com/zephir-lang/zephir/issues/2280)
- Added support for `mixed` [#2276](https://github.com/zephir-lang/zephir/issues/2276)

### Fixed
- Fixed multiple return types in stubs [#2283](https://github.com/zephir-lang/zephir/issues/2283)
Expand Down
56 changes: 43 additions & 13 deletions Library/ArgInfoDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ArgInfoDefinition
/**
* @var string
*/
private string $name = '';
private string $name;

/**
* @var ClassMethodParameters|null
Expand Down Expand Up @@ -58,14 +58,14 @@ class ArgInfoDefinition
/**
* ArgInfoDefinition constructor.
*
* @param $name
* @param string $name
* @param ClassMethod $functionLike
* @param CodePrinter $codePrinter
* @param CompilationContext $compilationContext
* @param bool $returnByRef
*/
public function __construct(
$name,
string $name,
ClassMethod $functionLike,
CodePrinter $codePrinter,
CompilationContext $compilationContext,
Expand Down Expand Up @@ -205,16 +205,41 @@ private function richRenderStart(): void
return;
}

$this->codePrinter->output(
sprintf(
'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)',
$this->name,
(int) $this->returnByRef,
$this->functionLike->getNumberOfRequiredParameters(),
$this->getReturnType(),
(int) $this->functionLike->areReturnTypesNullCompatible()
)
);
if ($this->functionLike->isMixed()) {
$this->codePrinter->output('#if PHP_VERSION_ID >= 80000');
$this->codePrinter->output(
sprintf(
'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, IS_MIXED, %d)',
$this->name,
(int) $this->returnByRef,
$this->functionLike->getNumberOfRequiredParameters(),
(int) $this->functionLike->areReturnTypesNullCompatible()
)
);
$this->codePrinter->output('#else');
$this->codePrinter->output(
sprintf(
'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)',
$this->name,
(int) $this->returnByRef,
$this->functionLike->getNumberOfRequiredParameters(),
$this->getReturnType(),
(int) $this->functionLike->areReturnTypesNullCompatible()
)
);
$this->codePrinter->output('#endif');
} else {
$this->codePrinter->output(
sprintf(
'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)',
$this->name,
(int) $this->returnByRef,
$this->functionLike->getNumberOfRequiredParameters(),
$this->getReturnType(),
(int) $this->functionLike->areReturnTypesNullCompatible()
)
);
}
}

private function renderEnd(): void
Expand Down Expand Up @@ -393,6 +418,11 @@ private function passByReference(array $parameter)

private function getReturnType(): string
{
// TODO: Come back here when PHP7.4 is deprecated.
/*if (array_key_exists('mixed', $this->functionLike->getReturnTypes())) {
return 'IS_MIXED';
}*/

if ($this->functionLike->areReturnTypesIntCompatible()) {
return 'IS_LONG';
}
Expand Down
18 changes: 11 additions & 7 deletions Library/Backends/ZendEngine3/Backend.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public function getStringsManager()
/**
* {@inheritdoc}
*/
public function getTypeDefinition($type)
public function getTypeDefinition($type): array
{
switch ($type) {
case 'zend_ulong':
Expand Down Expand Up @@ -155,6 +155,7 @@ public function getTypeDefinition($type)
case 'variable':
case 'array':
case 'null':
case 'mixed':
$pointer = '*';
$code = 'zval';
break;
Expand Down Expand Up @@ -317,7 +318,7 @@ public function onPostCompile(ClassMethod $method, CompilationContext $context)

public function generateInitCode(&$groupVariables, $type, $pointer, Variable $variable)
{
$isComplex = \in_array($type, ['variable', 'string', 'array', 'resource', 'callable', 'object'], true);
$isComplex = \in_array($type, ['variable', 'string', 'array', 'resource', 'callable', 'object', 'mixed'], true);

if ($isComplex && !$variable->isDoublePointer()) { /* && $variable->mustInitNull() */
$groupVariables[] = $variable->getName();
Expand Down Expand Up @@ -366,6 +367,7 @@ public function generateInitCode(&$groupVariables, $type, $pointer, Variable $va
case 'resource':
case 'callable':
case 'object':
case 'mixed':
$groupVariables[] = $pointer.$variable->getName();
break;

Expand Down Expand Up @@ -595,7 +597,7 @@ public function addArrayEntry(Variable $variable, $key, $value, CompilationConte
$keyType = 'append';
} elseif ($key instanceof CompiledExpression) {
$typeKey = $key->getType();
if ('variable' == $typeKey) {
if ('variable' === $typeKey || 'mixed' === $typeKey) {
$var = $context->symbolTable->getVariableForRead($key->getCode(), $context);
$typeKey = $var->getType();
}
Expand Down Expand Up @@ -630,6 +632,7 @@ public function addArrayEntry(Variable $variable, $key, $value, CompilationConte

case 'variable':
case 'array':
case 'mixed':
$type = 'zval';
break;
}
Expand Down Expand Up @@ -847,20 +850,21 @@ public function resolveValue($value, CompilationContext $context, $usePointer =
$tempVariable = new Variable('variable', $varName, $context->branchManager->getCurrentBranch());
$context->symbolTable->addRawVariable($tempVariable);
}

$tempVariable = $context->symbolTable->getVariableForWrite($varName, $context);
$tempVariable->increaseUses();
$tempVariable->setUsed(true, null);
$tempVariable->setUsed(true);

if ('null' == $value) {
$tempVariable->setDynamicTypes('null');
} else {
$tempVariable->setDynamicTypes('bool');
}

$value = $this->getVariableCode($tempVariable);
} else {
if ($value instanceof CompiledExpression) {
if ('array' == $value->getType()) {
$value = $context->symbolTable->getVariableForWrite($value->getCode(), $context, null);
} elseif ('variable' == $value->getType()) {
if (in_array($value->getType(), ['array', 'variable', 'mixed'])) {
$value = $context->symbolTable->getVariableForWrite($value->getCode(), $context);
} else {
return $value->getCode();
Expand Down
71 changes: 50 additions & 21 deletions Library/ClassMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ class ClassMethod
*/
protected bool $void = false;

/**
* Whether the variable is mixed.
*
* Only for PHP >= 8.0
*
* @var bool
*/
protected bool $mixed = false;

/**
* Whether the method is public or not.
*
Expand Down Expand Up @@ -276,34 +285,42 @@ public function setReturnTypes(?array $returnType = null): void
$castTypes = [];

foreach ($returnType['list'] as $returnTypeItem) {
if (isset($returnTypeItem['cast'])) {
if (isset($returnTypeItem['cast']['collection'])) {
continue;
}
/**
* We continue the loop, because it only works for PHP >= 8.0.
*/
if (isset($returnTypeItem['data-type']) && $returnTypeItem['data-type'] === 'mixed') {
$this->mixed = true;
}

if (isset($returnTypeItem['collection']) && $returnTypeItem['collection']) {
$types['array'] = [
'type' => 'return-type-parameter',
'data-type' => 'array',
'mandatory' => 0,
'file' => $returnTypeItem['cast']['file'],
'line' => $returnTypeItem['cast']['line'],
'char' => $returnTypeItem['cast']['char'],
];
} else {
$castTypes[$returnTypeItem['cast']['value']] = $returnTypeItem['cast']['value'];
}
} else {
if (!isset($returnTypeItem['cast'])) {
$types[$returnTypeItem['data-type']] = $returnTypeItem;
continue;
}

if (isset($returnTypeItem['cast']['collection'])) {
continue;
}

if (isset($returnTypeItem['collection']) && $returnTypeItem['collection']) {
$types['array'] = [
'type' => 'return-type-parameter',
'data-type' => 'array',
'mandatory' => 0,
'file' => $returnTypeItem['cast']['file'],
'line' => $returnTypeItem['cast']['line'],
'char' => $returnTypeItem['cast']['char'],
];
} else {
$castTypes[$returnTypeItem['cast']['value']] = $returnTypeItem['cast']['value'];
}
}

if (count($castTypes)) {
if (count($castTypes) > 0) {
$types['object'] = [];
$this->returnClassTypes = $castTypes;
}

if (count($types)) {
if (count($types) > 0) {
$this->returnTypes = $types;
}
}
Expand Down Expand Up @@ -515,7 +532,7 @@ public function setupOptimized(CompilationContext $compilationContext): self
return $this;
}

public function getOptimizedMethod()
public function getOptimizedMethod(): ClassMethod
{
$optimizedName = $this->getName().'_zephir_internal_call';
$optimizedMethod = $this->classDefinition->getMethod($optimizedName, false);
Expand Down Expand Up @@ -850,6 +867,16 @@ public function isVoid(): bool
return $this->void;
}

/**
* Checks if the methods return type is `mixed`.
*
* @return bool
*/
public function isMixed(): bool
{
return $this->mixed;
}

/**
* Checks if the method is inline.
*
Expand Down Expand Up @@ -1427,7 +1454,7 @@ public function assignZvalValue(array $parameter, CompilationContext $compilatio
{
$dataType = $this->getParamDataType($parameter);

if (in_array($dataType, ['variable', 'callable', 'object', 'resource'])) {
if (in_array($dataType, ['variable', 'callable', 'object', 'resource', 'mixed'])) {
return '';
}

Expand Down Expand Up @@ -1634,6 +1661,7 @@ public function compile(CompilationContext $compilationContext): void
case 'callable':
case 'resource':
case 'variable':
case 'mixed':
$symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
/* TODO: Move this to the respective backend, which requires refactoring how this works */
if ($compilationContext->backend->isZE3()) {
Expand Down Expand Up @@ -1860,6 +1888,7 @@ public function compile(CompilationContext $compilationContext): void
case 'callable':
case 'resource':
case 'variable':
case 'mixed':
$name = $parameter['name'];
break;

Expand Down
1 change: 1 addition & 0 deletions Library/ClassMethodParameters.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public function fetchParameters(bool $isMethodInternal): array
case 'callable':
case 'resource':
case 'variable':
case 'mixed':
$parameters[] = $isMethodInternal ? $name : '&'.$name;
break;

Expand Down
4 changes: 3 additions & 1 deletion Library/Exception/InvalidTypeException.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Zephir\Exception;

final class InvalidTypeException extends CompilerException
Expand All @@ -19,7 +21,7 @@ final class InvalidTypeException extends CompilerException
* @param string $type
* @param array|null $expression
*/
public function __construct($type, array $expression = null)
public function __construct(string $type, array $expression = null)
{
$message = sprintf(
'Returning type: %s but this type is not compatible with return-type hints declared in the method',
Expand Down
4 changes: 3 additions & 1 deletion Library/Expression.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public function setEvalMode(bool $evalMode): void
/**
* Compiles foo = [].
*
* @param array $expression
* @param array $expression
* @param CompilationContext $compilationContext
*
* @return CompiledExpression
Expand Down Expand Up @@ -231,6 +231,8 @@ public function emptyArray(array $expression, CompilationContext $compilationCon
*
* @param CompilationContext $compilationContext
*
* @throws CompilerException|Exception
*
* @return CompiledExpression
*
* @throws Exception
Expand Down
Loading

0 comments on commit a41cb40

Please sign in to comment.