VKCOM/kphp

Inferring during transpilation

Opened this issue · 0 comments

If I do someting like:

EmulatedEnumInt.php:

<?php
class EmulatedEnumInt {

 protected int $value;

 protected function __construct(int $value) {

  $this->value = $value;
 }

 public function __toString() : string {
  return (string) $this->value;
 }
}

CfgType.php:

<?php
#ifndef KPHP
enum CfgType : int {
case STRING = 1;
case INT = 2;
case BOOL = 3;
case ARRAY  = 4;
}
if (false) {
 #endif
 final class CfgType extends EmulatedEnumInt
 {
  const STRING = 1;
  const INT = 2;
  const BOOL = 3;
  const ARRAY  = 4;

  // GENERATED
  public static function STRING() : static {
   return new static(1);
  }

  public static function INT() : static {
   return new static(2);
  }

  public static function BOOL() : static {
   return new static(3);
  }

  public static function ARRAY() : static {
   return new static(4);
  }

  private static $fromMap = [
   1       =>      'STRING',
   2       =>      'INT',
   3       =>      'BOOL',
   4       =>      'ARRAY',
  ];

  public static function tryFrom(string|int $value) : static {
   if (isset(self::$fromMap[$value])) return new static(self::$fromMap[$value]);
   throw new \Exception("No such enum value:".$value);
  }
 }
 #ifndef KPHP
}
#endif

code.php:

<?php
include_once('CfgType.php');

#ifndef KPHP
/**
 * Get a configuration value (PHP VERSION)
 *
 * (Assumes Config object is already stored in Memcache (in KEY: '___PROJECT_SHORT____CONFIG') )
 *
 * @param string $settingName
 * @param CfgType $type
 * @param int $ttl
 *
 * @return ?int|?string|boolean|array
 */
function cfg(string $settingName, CfgType $type = CfgType::STRING, int $ttl = 86400) {

 switch($type) {

 case CfgType::STRING:
  $value = (time() % 2 === 0) ? NULL : (string)$settingName;
  return $value;

 case CfgType::INT:
  $value = (time() % 2 === 0) ? NULL : (int)80;
  return $value;

 case CfgType::BOOL:
  $value = (time() % 2 === 0) ? NULL : (bool) $settingName;
  return $value;

 case CfgType::ARRAY:
  $value = (time() % 2 === 0) ? NULL : ['foo', 'bar'];
  return $value;
 }
}

if (false) {
 #endif
 /**
  * Get a configuration value (KPHP VERSION)
  *
  * (Assumes Config object is already stored in Memcache (in KEY: '___PROJECT_SHORT____CONFIG') )
  *
  * @param string $settingName
  * @param CfgType $type
  * @param int $ttl
  *
  * @return ?int|?string|boolean|array
  */
 function cfg(string $settingName, CfgType $type, int $ttl = 86400) {

  $value = (string) $type;

  switch($value) {

  case (string)CfgType::STRING():
   $value = (time() % 2 === 0) ? NULL : (string)$settingName;
   return $value;

  case (string)CfgType::INT():
   $value = (time() % 2 === 0) ? NULL : (int)80;
   return $value;

  case (string)CfgType::BOOL():
   $value = (time() % 2 === 0) ? NULL : (bool)$settingName;
   return (bool) $settingName;

  case (string)CfgType::ARRAY():
   $value = (time() % 2 === 0) ? NULL : (array) ['foo', 'bar'];
   return $value;

  default:
   return (string) $settingName.'_default';
  }
 }

 #ifndef KPHP
}
#endif

main.php:

<?php
#ifndef KPHP // PHP
define('KPHP_VERSION', 0);
if (false)
#endif // KPHP
define('KPHP_VERSION', 1);

include('EmulatedEnumInt.php');
include('CfgType.php');
include('code.php');
 
$user = (KPHP_VERSION) ? cfg('USER', CfgType::STRING()) : cfg('USER', CfgType::STRING);

echo "User=".$user.PHP_EOL; 

to cater for the fact that enums are not supported in KPHP (the code base I am trying to convert uses many enums), I would expect the transpiler to be able to deduce that the above statement can be re-written as:

$user = cfg('USER', CfgType::STRING());

since KPHP_VERSION is defined to be 1 when compiling with KPHP (and the else part of the ternary will only be taken when running in a PHP context)

However, if I compile like this:

# kphp main.php --mode server --include-dir $(pwd) -o ./server

I get this error:

Compilation error at stage: Calc actual calls, gen by type-inferer.cpp:53
 main.php:14  in global scope
   $user = (KPHP_VERSION) ? cfg('USER', CfgType::STRING()) : cfg('USER', CfgType::STRING);

pass int to argument $type of cfg
but it's declared as @param CfgType

 main.php:14  in global scope
   $user = (KPHP_VERSION) ? cfg('USER', CfgType::STRING()) : cfg('USER', CfgType::STRING);
   1 is int

apparently because it takes the else part into account ?

I would like to be able to do assignments like ($user = ) on a single line.
However, I am aware that the $user = ... line can be replaced by:

#ifndef KPHP // PHP
$user = cfg('USER', CfgType::STRING);
if (FALSE) { // KPHP
#endif
$user = cfg('USER', CfgType::STRING());
#ifndef KPHP // PHP
}
#endif 

but that syntax is rather cumbersome ...

So can't the transpiler be improved in this regard ?