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

#2276 - Add support for mixed type #2288

Merged
merged 8 commits into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from all 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 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