fix(gen_stub): spell PHP_INT_MIN constants with ZEND_LONG_MIN (#48) --skip-tests

getCExpr() emitted int class constants, property defaults and parameter
defaults with strval(), so `const M = PHP_INT_MIN;` produced
`ZVAL_LONG(&const_M_value, -9223372036854775808)`. C parses that as
unary minus applied to the literal 9223372036854775808, which exceeds
long long and is ill-formed, so the generated extension source does not
compile. The expression path already handles this via genIntegerLiteral
(ZEND_LONG_MIN); give the stub metadata path the same spelling.

The float paths (17-digit round-trip, -0.0 sign, INF/NAN) were already
fixed upstream in 2d81626a; the new test pins those literals down
together with the int boundary values.
master
Alessio Giacobbe 3 days ago committed by GitHub
parent e259b3fbd1
commit 284abddba6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 16
      phpunit/code/int-min-constant-metadata.php
  2. 41
      phpunit/src/ConstantMetadataLiteralTest.php
  3. 7
      src/gen_stub.php

@ -0,0 +1,16 @@
<?php
class IntMinConstants
{
const MIN = PHP_INT_MIN;
const MAX = PHP_INT_MAX;
const NEGZ = -0.0;
const PI = M_PI;
public int $floor = PHP_INT_MIN;
}
function useIntMinDefault(int $x = PHP_INT_MIN): int
{
return $x;
}

@ -0,0 +1,41 @@
<?php
use TypePhp\CompilerTest;
/**
* Class-constant and property-default metadata must embed scalar values with
* exact, well-formed C literals: PHP_INT_MIN cannot be spelled as one
* negative literal ("-9223372036854775808" negates an out-of-range positive
* literal and is ill-formed), -0.0 must keep its sign, and doubles must
* round-trip at 17 significant digits.
*/
final class ConstantMetadataLiteralTest extends \BaseTest
{
public function testIntMinAndFloatConstantsEmitExactLiterals(): void
{
$previous = ini_set('precision', '14');
try {
global $translator;
$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH);
$translator = $compiler;
$testFile = TYPEPHP_ROOT_PATH . '/phpunit/code/int-min-constant-metadata.php';
$compiler->addFiles([$testFile]);
$compiler->prepareFile($testFile);
$compiler->convertFile($testFile);
$arginfoHeader = $compiler->getArgInfoHeaderFile($testFile);
$arginfo = file_get_contents($arginfoHeader);
} finally {
if ($previous !== false) {
ini_set('precision', $previous);
}
}
self::assertIsString($arginfo);
self::assertStringContainsString('ZVAL_LONG(&const_MIN_value, ZEND_LONG_MIN);', $arginfo);
self::assertStringContainsString('ZVAL_LONG(&const_MAX_value, 9223372036854775807);', $arginfo);
self::assertStringContainsString('ZVAL_LONG(&property_floor_default_value, ZEND_LONG_MIN);', $arginfo);
self::assertStringContainsString('ZVAL_DOUBLE(&const_NEGZ_value, -0.0);', $arginfo);
self::assertStringContainsString('ZVAL_DOUBLE(&const_PI_value, 3.1415926535897931);', $arginfo);
self::assertStringNotContainsString('-9223372036854775808', $arginfo);
}
}

@ -2828,6 +2828,13 @@ class EvaluatedValue
// leaking heredoc/nowdoc source syntax into generated C++. // leaking heredoc/nowdoc source syntax into generated C++.
return '"' . getTranslator()->escapeString((string) $this->value) . '"'; return '"' . getTranslator()->escapeString((string) $this->value) . '"';
} elseif ($this->type->isInt()) { } elseif ($this->type->isInt()) {
// PHP_INT_MIN cannot be spelled as one negative literal: C parses
// "-9223372036854775808" as negation applied to an out-of-range
// positive literal, which is ill-formed. Reuse the ZEND_LONG_MIN
// macro, exactly like the expression path (genIntegerLiteral).
if ($this->value === PHP_INT_MIN) {
return 'ZEND_LONG_MIN';
}
return strval($this->value); return strval($this->value);
} elseif ($this->type->isFloat()) { } elseif ($this->type->isFloat()) {
return getTranslator()->genFloatLiteral((float) $this->value); return getTranslator()->genFloatLiteral((float) $this->value);

Loading…
Cancel
Save