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 24 hours 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++.
return '"' . getTranslator()->escapeString((string) $this->value) . '"';
} 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);
} elseif ($this->type->isFloat()) {
return getTranslator()->genFloatLiteral((float) $this->value);

Loading…
Cancel
Save