Skip to content

Commit

Permalink
Standard library for shapes - runtime support
Browse files Browse the repository at this point in the history
Summary: Shapes are integral part of the language, but they are missing even basic support in standard library - in runtime, the array functions work with them (since they are arrays under the hood), but if those array functions are typed in .hhi files (as taking array, KeyedContainer, Indexish, etc.) the typechecker will balk because there is no implicit subtyping relation between shapes and arrays (by design).

They also can't be implemented in user space, because we are missing a way to express things like "this function accesses shape field" using type hints.

We could invent a completely new notations to express it, but since shapes are relatively simple features - you can basically only set, access or unset a field - we decided to start by hardcoding a handful of those functions in the typechecker in the form of Shapes class, mimicking helper functions in Vectors, Sets etc. - see {D2116812} for example. I make it auto imported in hh files because we plan to consider them a part of the language now. In particular, {D2094934} introduces an error message forbidding accessing optional fields in any other way than using functions from this class.

In runtime they are implemented as simple wrappers around array functions.

Reviewed By: @jwatzman

Differential Revision: D2154441
  • Loading branch information
dabek authored and hhvm-bot committed Jun 19, 2015
1 parent 8676035 commit a78c669
Show file tree
Hide file tree
Showing 39 changed files with 915 additions and 798 deletions.
1 change: 1 addition & 0 deletions hphp/compiler/parser/parser.cpp
Expand Up @@ -2422,6 +2422,7 @@ hphp_string_imap<std::string> Parser::getAutoAliasedClassesHelper() {
(AliasEntry){"ImmSet", "HH\\ImmSet"},
(AliasEntry){"InvariantException", "HH\\InvariantException"},
(AliasEntry){"IMemoizeParam", "HH\\IMemoizeParam"},
(AliasEntry){"Shapes", "HH\\Shapes"},

(AliasEntry){"Awaitable", "HH\\Awaitable"},
(AliasEntry){"AsyncGenerator", "HH\\AsyncGenerator"},
Expand Down
1 change: 1 addition & 0 deletions hphp/system/php.txt
Expand Up @@ -104,6 +104,7 @@ hphp/system/php/password/password.php
hphp/system/php/pdo/PDOException.php
hphp/system/php/redis/Redis.php
hphp/system/php/redis/RedisSessionModule.php
hphp/system/php/shapes/Shapes.php
hphp/system/php/soap/SoapFault.php
hphp/system/php/spl/datastructures/SplFixedArray.php
hphp/system/php/spl/datastructures/SplObjectStorage.php
Expand Down
34 changes: 34 additions & 0 deletions hphp/system/php/shapes/Shapes.php
@@ -0,0 +1,34 @@
<?hh

namespace HH {
abstract final class Shapes {

public static function idx(
array $shape,
arraykey $index,
$default = null,
) {
return hphp_array_idx($shape, $index, $default);
}

public static function keyExists(
array $shape,
arraykey $index,
): bool {
return array_key_exists($index, $shape);
}

public static function removeKey(
array &$shape,
arraykey $index,
): void {
unset($shape[$index]);
}

public static function toArray(
array $shape,
): array {
return $shape;
}
}
}
14 changes: 7 additions & 7 deletions hphp/test/quick/hh_async_closure_return_type_0.php
Expand Up @@ -56,9 +56,9 @@ function f8() {
function f8_soft() {
return async function ($p): @Awaitable<callable> { return $p; };
}
function f9() { return async function ($p): Awaitable<Shapes> { return $p; }; }
function f9() { return async function ($p): Awaitable<Figure> { return $p; }; }
function f9_soft() {
return async function ($p): @Awaitable<Shapes> { return $p; };
return async function ($p): @Awaitable<Figure> { return $p; };
}
function f10() { return async function ($p): Awaitable<Square> { return $p; }; }
function f10_soft() {
Expand Down Expand Up @@ -115,9 +115,9 @@ function f20_soft() {
return async function ($p): @Awaitable<callable> { return $p; };
}

class Shapes {}
class Square extends Shapes {}
class Fractal<T> extends Shapes {}
class Figure {}
class Square extends Figure {}
class Fractal<T> extends Figure {}
class :div {}

class A {}
Expand Down Expand Up @@ -177,7 +177,7 @@ function main() {
call_wrapper($f(), imagecreate(10, 10));
call_wrapper($f(), array());
call_wrapper($f(), function($x){return $x*$x;});
call_wrapper($f(), new Shapes());
call_wrapper($f(), new Figure());
call_wrapper($f(), new Square());
call_wrapper($f(), new Fractal());
call_wrapper($f(), <div/>);
Expand Down Expand Up @@ -219,7 +219,7 @@ function main() {
call_wrapper($f(), imagecreate(10, 10));
call_wrapper($f(), array());
call_wrapper($f(), function($x){return $x*$x;});
call_wrapper($f(), new Shapes());
call_wrapper($f(), new Figure());
call_wrapper($f(), new Square());
call_wrapper($f(), new Fractal());
call_wrapper($f(), <div/>);
Expand Down
14 changes: 7 additions & 7 deletions hphp/test/quick/hh_async_closure_return_type_1.php
Expand Up @@ -56,9 +56,9 @@ function f8() {
function f8_soft() {
return async function ($p): @Awaitable<callable> { return $p; };
}
function f9() { return async function ($p): Awaitable<Shapes> { return $p; }; }
function f9() { return async function ($p): Awaitable<Figure> { return $p; }; }
function f9_soft() {
return async function ($p): @Awaitable<Shapes> { return $p; };
return async function ($p): @Awaitable<Figure> { return $p; };
}
function f10() { return async function ($p): Awaitable<Square> { return $p; }; }
function f10_soft() {
Expand Down Expand Up @@ -115,9 +115,9 @@ function f20_soft() {
return async function ($p): @Awaitable<callable> { return $p; };
}

class Shapes {}
class Square extends Shapes {}
class Fractal<T> extends Shapes {}
class Figure {}
class Square extends Figure {}
class Fractal<T> extends Figure {}
class :div {}

class A {}
Expand Down Expand Up @@ -177,7 +177,7 @@ function main() {
call_wrapper($f(), imagecreate(10, 10));
call_wrapper($f(), array());
call_wrapper($f(), function($x){return $x*$x;});
call_wrapper($f(), new Shapes());
call_wrapper($f(), new Figure());
call_wrapper($f(), new Square());
call_wrapper($f(), new Fractal());
call_wrapper($f(), <div/>);
Expand Down Expand Up @@ -219,7 +219,7 @@ function main() {
call_wrapper($f(), imagecreate(10, 10));
call_wrapper($f(), array());
call_wrapper($f(), function($x){return $x*$x;});
call_wrapper($f(), new Shapes());
call_wrapper($f(), new Figure());
call_wrapper($f(), new Square());
call_wrapper($f(), new Fractal());
call_wrapper($f(), <div/>);
Expand Down

0 comments on commit a78c669

Please sign in to comment.