From 6e8178da165b8ec573cd3b23aa268857218d609f Mon Sep 17 00:00:00 2001 From: drussi Date: Sat, 13 Apr 2013 18:26:12 -0700 Subject: [PATCH] expose type annotation to reflection including generics, function types and tuples This is intended so reflection can be used (via getTypehintText and getReturnTypehintText) to regenerate code the user annotated with types. Essentially using reflection to intrispect code in order to generate type safe (hack safe) code. That is particularly important for the tools that do dependency injection. The runtime should be oblivious to the change as the rich type annotation is currently only stored for the sake of reflection. For functions the values are in the shared portion which is cold and should also take care of traits. --- hphp/compiler/analysis/emitter.cpp | 3 + .../expression/parameter_expression.cpp | 17 +- .../expression/parameter_expression.h | 12 +- hphp/compiler/parser/hphp.tab.cpp | 129 +++++++----- hphp/compiler/parser/parser.cpp | 71 ++++++- hphp/compiler/parser/parser.h | 12 ++ hphp/compiler/type_annotation.cpp | 141 ++++++++++++++ hphp/compiler/type_annotation.h | 102 ++++++++++ hphp/runtime/ext/ext_reflection.cpp | 4 +- hphp/runtime/vm/func.cpp | 1 + hphp/runtime/vm/func.h | 11 +- .../slow/reflection_classes/1363.php.expect | 4 +- hphp/test/test_code_run.cpp | 183 +++++++++++++++++- hphp/util/parser/hphp.y | 57 +++--- hphp/util/parser/test/hphp.tab.cpp | 129 +++++++----- hphp/util/parser/test/parser.h | 12 ++ 16 files changed, 735 insertions(+), 153 deletions(-) create mode 100644 hphp/compiler/type_annotation.cpp create mode 100644 hphp/compiler/type_annotation.h diff --git a/hphp/compiler/analysis/emitter.cpp b/hphp/compiler/analysis/emitter.cpp index 78a0aa913..7dd284a63 100644 --- a/hphp/compiler/analysis/emitter.cpp +++ b/hphp/compiler/analysis/emitter.cpp @@ -5229,6 +5229,9 @@ void EmitterVisitor::emitPostponedMeths() { pi.setTypeConstraint(tc); TRACE(1, "Added constraint to %s\n", fe->name()->data()); } + if (par->hasUserType()) { + pi.setUserType(StringData::GetStaticString(par->getUserTypeHint())); + } // Store info about the default value if there is one. if (par->isOptional()) { const StringData* phpCode; diff --git a/hphp/compiler/expression/parameter_expression.cpp b/hphp/compiler/expression/parameter_expression.cpp index 75318d56d..94ef1f643 100644 --- a/hphp/compiler/expression/parameter_expression.cpp +++ b/hphp/compiler/expression/parameter_expression.cpp @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ */ +#include #include #include #include @@ -31,12 +32,12 @@ using namespace HPHP; ParameterExpression::ParameterExpression (EXPRESSION_CONSTRUCTOR_PARAMETERS, - const std::string &type, const std::string &name, bool ref, + TypeAnnotationPtr type, const std::string &name, bool ref, ExpressionPtr defaultValue, ExpressionPtr attributeList) : Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ParameterExpression)), m_originalType(type), m_name(name), m_ref(ref), m_defaultValue(defaultValue), m_attributeList(attributeList) { - m_type = Util::toLower(type); + m_type = Util::toLower(type ? type->simpleName() : ""); if (m_defaultValue) { m_defaultValue->setContext(InParameterExpression); } @@ -50,6 +51,16 @@ ExpressionPtr ParameterExpression::clone() { return exp; } +const std::string ParameterExpression::getOriginalTypeHint() const { + assert(hasTypeHint()); + return m_originalType->simpleName(); +} + +const std::string ParameterExpression::getUserTypeHint() const { + assert(hasUserType()); + return m_originalType->fullName(); +} + /////////////////////////////////////////////////////////////////////////////// // parser functions @@ -275,7 +286,7 @@ void ParameterExpression::compatibleDefault() { // code generation functions void ParameterExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) { - if (!m_type.empty()) cg_printf("%s ", m_originalType.c_str()); + if (!m_type.empty()) cg_printf("%s ", m_originalType->simpleName().c_str()); if (m_ref) cg_printf("&"); cg_printf("$%s", m_name.c_str()); if (m_defaultValue) { diff --git a/hphp/compiler/expression/parameter_expression.h b/hphp/compiler/expression/parameter_expression.h index 0a207ae0f..65a55435b 100644 --- a/hphp/compiler/expression/parameter_expression.h +++ b/hphp/compiler/expression/parameter_expression.h @@ -26,11 +26,12 @@ namespace HPHP { DECLARE_BOOST_TYPES(Type); DECLARE_BOOST_TYPES(AnalysisResult); DECLARE_BOOST_TYPES(ParameterExpression); +DECLARE_BOOST_TYPES(TypeAnnotation); class ParameterExpression : public Expression { public: ParameterExpression(EXPRESSION_CONSTRUCTOR_PARAMETERS, - const std::string &type, const std::string &name, + TypeAnnotationPtr type, const std::string &name, bool ref, ExpressionPtr defaultValue, ExpressionPtr attributeList); @@ -49,10 +50,9 @@ public: assert(hasTypeHint()); return m_type; } - const std::string &getOriginalTypeHint() const { - assert(hasTypeHint()); - return m_originalType; - } + bool hasUserType() const { return m_originalType != nullptr; } + const std::string getOriginalTypeHint() const; + const std::string getUserTypeHint() const; void parseHandler(ClassScopePtr cls); void compatibleDefault(); void fixupSelfAndParentTypehints(ClassScopePtr cls); @@ -60,7 +60,7 @@ private: TypePtr getTypeSpecForClass(AnalysisResultPtr ar, bool forInference); std::string m_type; - std::string m_originalType; + TypeAnnotationPtr m_originalType; std::string m_name; bool m_ref; ExpressionPtr m_defaultValue; diff --git a/hphp/compiler/parser/hphp.tab.cpp b/hphp/compiler/parser/hphp.tab.cpp index d5cf43625..cf151b88d 100644 --- a/hphp/compiler/parser/hphp.tab.cpp +++ b/hphp/compiler/parser/hphp.tab.cpp @@ -1626,10 +1626,10 @@ static const yytype_uint16 yyrline[] = 2261, 2264, 2267, 2269, 2271, 2275, 2276, 2278, 2279, 2285, 2286, 2288, 2290, 2292, 2294, 2297, 2298, 2299, 2303, 2304, 2305, 2306, 2307, 2308, 2309, 2313, 2314, 2318, 2327, 2332, - 2333, 2339, 2340, 2348, 2351, 2355, 2356, 2360, 2361, 2362, - 2363, 2367, 2368, 2372, 2373, 2374, 2376, 2378, 2379, 2383, - 2389, 2391, 2395, 2398, 2401, 2410, 2411, 2412, 2423, 2424, - 2425, 2428, 2431, 2432, 2435, 2440, 2441 + 2333, 2339, 2340, 2348, 2351, 2355, 2358, 2363, 2364, 2365, + 2366, 2370, 2371, 2375, 2376, 2377, 2379, 2381, 2382, 2386, + 2392, 2394, 2398, 2401, 2404, 2413, 2416, 2419, 2420, 2423, + 2424, 2428, 2433, 2437, 2443, 2451, 2452 }; #endif @@ -10172,101 +10172,118 @@ yyreduce: { (yyval).reset(); ;} break; + case 675: + +/* Line 1455 of yacc.c */ +#line 2355 "../../../hphp/util/parser/hphp.y" + { Token t; t.reset(); + _p->onTypeList((yyvsp[(1) - (1)]), t); + (yyval) = (yyvsp[(1) - (1)]); ;} + break; + + case 676: + +/* Line 1455 of yacc.c */ +#line 2358 "../../../hphp/util/parser/hphp.y" + { _p->onTypeList((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); + (yyval) = (yyvsp[(1) - (3)]); ;} + break; + case 677: /* Line 1455 of yacc.c */ -#line 2360 "../../../hphp/util/parser/hphp.y" - { (yyval).reset(); ;} +#line 2363 "../../../hphp/util/parser/hphp.y" + { (yyval) = (yyvsp[(1) - (3)]); ;} break; case 678: /* Line 1455 of yacc.c */ -#line 2361 "../../../hphp/util/parser/hphp.y" - { (yyval).reset(); ;} +#line 2364 "../../../hphp/util/parser/hphp.y" + { (yyval) = (yyvsp[(1) - (1)]); ;} break; case 679: /* Line 1455 of yacc.c */ -#line 2362 "../../../hphp/util/parser/hphp.y" +#line 2365 "../../../hphp/util/parser/hphp.y" { (yyval).reset(); ;} break; case 680: /* Line 1455 of yacc.c */ -#line 2363 "../../../hphp/util/parser/hphp.y" +#line 2366 "../../../hphp/util/parser/hphp.y" { (yyval).reset(); ;} break; case 681: /* Line 1455 of yacc.c */ -#line 2367 "../../../hphp/util/parser/hphp.y" +#line 2370 "../../../hphp/util/parser/hphp.y" { (yyval).reset(); ;} break; case 682: /* Line 1455 of yacc.c */ -#line 2368 "../../../hphp/util/parser/hphp.y" +#line 2371 "../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); (yyval) = (yyvsp[(2) - (2)]); ;} break; case 683: /* Line 1455 of yacc.c */ -#line 2372 "../../../hphp/util/parser/hphp.y" +#line 2375 "../../../hphp/util/parser/hphp.y" { _p->addTypeVar((yyvsp[(1) - (3)]).text()); ;} break; case 684: /* Line 1455 of yacc.c */ -#line 2373 "../../../hphp/util/parser/hphp.y" +#line 2376 "../../../hphp/util/parser/hphp.y" { _p->addTypeVar((yyvsp[(1) - (1)]).text()); ;} break; case 686: /* Line 1455 of yacc.c */ -#line 2377 "../../../hphp/util/parser/hphp.y" +#line 2380 "../../../hphp/util/parser/hphp.y" { _p->addTypeVar((yyvsp[(1) - (5)]).text()); ;} break; case 687: /* Line 1455 of yacc.c */ -#line 2378 "../../../hphp/util/parser/hphp.y" +#line 2381 "../../../hphp/util/parser/hphp.y" { _p->addTypeVar((yyvsp[(1) - (3)]).text()); ;} break; case 689: /* Line 1455 of yacc.c */ -#line 2385 "../../../hphp/util/parser/hphp.y" +#line 2388 "../../../hphp/util/parser/hphp.y" { validate_shape_keyname((yyvsp[(1) - (3)]), _p); ;} break; case 692: /* Line 1455 of yacc.c */ -#line 2396 "../../../hphp/util/parser/hphp.y" +#line 2399 "../../../hphp/util/parser/hphp.y" { (yyval) = (yyvsp[(1) - (2)]); ;} break; case 693: /* Line 1455 of yacc.c */ -#line 2398 "../../../hphp/util/parser/hphp.y" +#line 2401 "../../../hphp/util/parser/hphp.y" {;} break; case 694: /* Line 1455 of yacc.c */ -#line 2402 "../../../hphp/util/parser/hphp.y" +#line 2405 "../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); (yyval).setText("array"); ;} break; @@ -10274,54 +10291,51 @@ yyreduce: case 695: /* Line 1455 of yacc.c */ -#line 2410 "../../../hphp/util/parser/hphp.y" - { only_in_strict_mode(_p); (yyval).reset(); ;} +#line 2413 "../../../hphp/util/parser/hphp.y" + { only_in_strict_mode(_p); + _p->onTypeSpecialization((yyvsp[(2) - (2)]), '?'); + (yyval) = (yyvsp[(2) - (2)]); ;} break; case 696: /* Line 1455 of yacc.c */ -#line 2411 "../../../hphp/util/parser/hphp.y" - { only_in_strict_mode(_p); (yyval).reset(); ;} +#line 2416 "../../../hphp/util/parser/hphp.y" + { only_in_strict_mode(_p); + _p->onTypeSpecialization((yyvsp[(2) - (2)]), '@'); + (yyval) = (yyvsp[(2) - (2)]); ;} break; case 697: /* Line 1455 of yacc.c */ -#line 2412 "../../../hphp/util/parser/hphp.y" - { (yyval) = (yyvsp[(1) - (2)]); - /* if the type annotation is a bound - typevar we have to strip it */ - if (_p->scanner().isStrictMode() && - (_p->isTypeVar((yyval).text()) || - !(yyval).text().compare("mixed") || - !(yyval).text().compare("this") - )) { - (yyval).reset(); - } - ;} +#line 2419 "../../../hphp/util/parser/hphp.y" + { _p->onTypeAnnotation((yyval), (yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); ;} break; case 698: /* Line 1455 of yacc.c */ -#line 2423 "../../../hphp/util/parser/hphp.y" - { (yyval).setText("array"); ;} +#line 2420 "../../../hphp/util/parser/hphp.y" + { Token t; t.reset(); + (yyvsp[(1) - (1)]).setText("array"); + _p->onTypeAnnotation((yyval), (yyvsp[(1) - (1)]), t); ;} break; case 699: /* Line 1455 of yacc.c */ -#line 2424 "../../../hphp/util/parser/hphp.y" +#line 2423 "../../../hphp/util/parser/hphp.y" { (yyval) = (yyvsp[(1) - (1)]); ;} break; case 700: /* Line 1455 of yacc.c */ -#line 2426 "../../../hphp/util/parser/hphp.y" +#line 2425 "../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); - (yyval).setText("array"); ;} + (yyvsp[(1) - (4)]).setText("array"); + _p->onTypeAnnotation((yyval), (yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); ;} break; case 701: @@ -10329,49 +10343,60 @@ yyreduce: /* Line 1455 of yacc.c */ #line 2429 "../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); - (yyval).setText("array"); ;} + _p->onTypeList((yyvsp[(3) - (6)]), (yyvsp[(5) - (6)])); + (yyvsp[(1) - (6)]).setText("array"); + _p->onTypeAnnotation((yyval), (yyvsp[(1) - (6)]), (yyvsp[(3) - (6)])); ;} break; case 702: /* Line 1455 of yacc.c */ -#line 2431 "../../../hphp/util/parser/hphp.y" - { (yyvsp[(1) - (1)]).xhpLabel(); (yyval) = (yyvsp[(1) - (1)]); ;} +#line 2433 "../../../hphp/util/parser/hphp.y" + { (yyvsp[(1) - (1)]).xhpLabel(); + Token t; t.reset(); + _p->onTypeAnnotation((yyval), (yyvsp[(1) - (1)]), t); + _p->onTypeSpecialization((yyval), 'x'); ;} break; case 703: /* Line 1455 of yacc.c */ -#line 2434 "../../../hphp/util/parser/hphp.y" - { only_in_strict_mode(_p); (yyval).reset(); ;} +#line 2439 "../../../hphp/util/parser/hphp.y" + { only_in_strict_mode(_p); + _p->onTypeList((yyvsp[(7) - (8)]), (yyvsp[(4) - (8)])); + _p->onTypeAnnotation((yyval), (yyvsp[(2) - (8)]), (yyvsp[(7) - (8)])); + _p->onTypeSpecialization((yyval), 'f'); ;} break; case 704: /* Line 1455 of yacc.c */ -#line 2435 "../../../hphp/util/parser/hphp.y" +#line 2443 "../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); - (yyval).setText("array"); ;} + _p->onTypeList((yyvsp[(2) - (5)]), (yyvsp[(4) - (5)])); + Token t; t.reset(); t.setText("array"); + _p->onTypeAnnotation((yyval), t, (yyvsp[(2) - (5)])); + _p->onTypeSpecialization((yyval), 's'); ;} break; case 705: /* Line 1455 of yacc.c */ -#line 2440 "../../../hphp/util/parser/hphp.y" +#line 2451 "../../../hphp/util/parser/hphp.y" { (yyval) = (yyvsp[(1) - (1)]); ;} break; case 706: /* Line 1455 of yacc.c */ -#line 2441 "../../../hphp/util/parser/hphp.y" +#line 2452 "../../../hphp/util/parser/hphp.y" { (yyval).reset(); ;} break; /* Line 1455 of yacc.c */ -#line 10374 "hphp.tab.cpp" +#line 10399 "hphp.tab.cpp" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); @@ -10591,7 +10616,7 @@ yyreturn: /* Line 1675 of yacc.c */ -#line 2444 "../../../hphp/util/parser/hphp.y" +#line 2455 "../../../hphp/util/parser/hphp.y" bool Parser::parseImpl() { return yyparse(this) == 0; diff --git a/hphp/compiler/parser/parser.cpp b/hphp/compiler/parser/parser.cpp index 3df1ed74d..1ba9a5091 100644 --- a/hphp/compiler/parser/parser.cpp +++ b/hphp/compiler/parser/parser.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include @@ -831,7 +832,7 @@ void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref, func = NEW_STMT(FunctionStatement, exp, ref->num(), closureName, dynamic_pointer_cast(new_params->exp), - ret.text(), + ret.typeAnnotationName(), dynamic_pointer_cast(stmt->stmt), attribute, comment, ExpressionListPtr()); out->stmt = func; @@ -879,8 +880,8 @@ void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref, attrList = dynamic_pointer_cast(attr->exp); } - func = NEW_STMT(FunctionStatement, exp, ref->num(), funcName, - old_params, ret.text(), + func = NEW_STMT(FunctionStatement, exp, ref->num(), funcName, old_params, + ret.typeAnnotationName(), dynamic_pointer_cast(stmt->stmt), attribute, comment, attrList); out->stmt = func; @@ -912,7 +913,8 @@ void Parser::onParam(Token &out, Token *params, Token &type, Token &var, if (attr && attr->exp) { attrList = dynamic_pointer_cast(attr->exp); } - expList->addElement(NEW_EXP(ParameterExpression, type->text(), var->text(), + TypeAnnotationPtr typeAnnotation = type.typeAnnotation; + expList->addElement(NEW_EXP(ParameterExpression, typeAnnotation, var->text(), ref, defValue ? defValue->exp : ExpressionPtr(), attrList)); out->exp = expList; @@ -1079,12 +1081,14 @@ void Parser::onClassVariableStart(Token &out, Token *modifiers, Token &decl, : NEW_EXP0(ModifierExpression); out->stmt = NEW_STMT - (ClassVariable, exp, (type) ? type->text() : "", + (ClassVariable, exp, + (type) ? type->typeAnnotationName() : "", dynamic_pointer_cast(decl->exp)); } else { out->stmt = - NEW_STMT(ClassConstant, (type) ? type->text() : "", - dynamic_pointer_cast(decl->exp)); + NEW_STMT(ClassConstant, + (type) ? type->typeAnnotationName() : "", + dynamic_pointer_cast(decl->exp)); } } @@ -1134,7 +1138,7 @@ void Parser::onMethod(Token &out, Token &modifiers, Token &ret, Token &ref, ModifierExpressionPtr exp2 = Construct::Clone(exp); mth = NEW_STMT(MethodStatement, exp2, ref->num(), closureName, dynamic_pointer_cast(new_params->exp), - ret.text(), + ret.typeAnnotationName(), dynamic_pointer_cast(stmt->stmt), attribute, comment, ExpressionListPtr()); out->stmt = mth; @@ -1166,7 +1170,9 @@ void Parser::onMethod(Token &out, Token &modifiers, Token &ret, Token &ref, attrList = dynamic_pointer_cast(attr->exp); } mth = NEW_STMT(MethodStatement, exp, ref->num(), name->text(), - old_params, ret.text(), stmts, attribute, comment, + old_params, + ret.typeAnnotationName(), + stmts, attribute, comment, attrList); out->stmt = mth; if (reloc) { @@ -1569,7 +1575,8 @@ void Parser::onClosureParam(Token &out, Token *params, Token ¶m, } else { expList = NEW_EXP0(ExpressionList); } - expList->addElement(NEW_EXP(ParameterExpression, "", param->text(), ref, + expList->addElement(NEW_EXP(ParameterExpression, TypeAnnotationPtr(), + param->text(), ref, ExpressionPtr(), ExpressionPtr())); out->exp = expList; } @@ -1586,6 +1593,50 @@ void Parser::onTypedef(Token& out, const Token& name, const Token& type) { out->stmt = NEW_STMT(TypedefStatement, name.text(), type.text()); } +void Parser::onTypeAnnotation(Token& out, const Token& name, + const Token& typeArgs) { + out.set(name.num(), name.text()); + out.typeAnnotation = TypeAnnotationPtr( + new TypeAnnotation(name.text(), typeArgs.typeAnnotation)); + if (isTypeVar(name.text())) { + out.typeAnnotation->setTypeVar(); + } +} + +void Parser::onTypeList(Token& type1, const Token& type2) { + if (!type1.typeAnnotation) { + PARSE_ERROR("Missing type in type list"); + } + if (type2.num() != 0 && !type1.typeAnnotation) { + PARSE_ERROR("Missing type in type list"); + } + if (type2.typeAnnotation) { + type1.typeAnnotation->appendToTypeList(type2.typeAnnotation); + } +} + +void Parser::onTypeSpecialization(Token& type, char specialization) { + if (type.typeAnnotation) { + switch (specialization) { + case '?': + type.typeAnnotation->setNullable(); + break; + case '@': + type.typeAnnotation->setSoft(); + break; + case 't': + type.typeAnnotation->setTuple(); + break; + case 'f': + type.typeAnnotation->setFunction(); + break; + case 'x': + type.typeAnnotation->setXHP(); + break; + } + } +} + void Parser::invalidateGoto(TStatementPtr stmt, GotoError error) { GotoStatement *gs = (GotoStatement*) stmt; assert(gs); diff --git a/hphp/compiler/parser/parser.h b/hphp/compiler/parser/parser.h index 5b9575ef7..406965623 100644 --- a/hphp/compiler/parser/parser.h +++ b/hphp/compiler/parser/parser.h @@ -21,6 +21,7 @@ #include "util/parser/parser.h" #include "compiler/construct.h" #include "compiler/option.h" +#include "compiler/type_annotation.h" #ifdef HPHP_PARSER_NS #undef HPHP_PARSER_NS @@ -49,6 +50,7 @@ DECLARE_BOOST_TYPES(StatementList); DECLARE_BOOST_TYPES(Location); DECLARE_BOOST_TYPES(AnalysisResult); DECLARE_BOOST_TYPES(BlockScope); +DECLARE_BOOST_TYPES(TypeAnnotation); namespace Compiler { /////////////////////////////////////////////////////////////////////////////// @@ -58,6 +60,7 @@ class Token : public ScannerToken { public: ExpressionPtr exp; StatementPtr stmt; + TypeAnnotationPtr typeAnnotation; Token &operator+(const char *str) { m_text += str; @@ -78,12 +81,17 @@ public: ScannerToken::operator=(other); exp = other.exp; stmt = other.stmt; + typeAnnotation = other.typeAnnotation; } void reset() { exp.reset(); stmt.reset(); + typeAnnotation.reset(); ScannerToken::reset(); } + const std::string typeAnnotationName() { + return (typeAnnotation) ? typeAnnotation->fullName() : ""; + } }; /////////////////////////////////////////////////////////////////////////////// @@ -238,6 +246,10 @@ public: void onGoto(Token &out, Token &label, bool limited); void onTypedef(Token& out, const Token& name, const Token& type); + void onTypeAnnotation(Token& out, const Token& name, const Token& typeArgs); + void onTypeList(Token& type1, const Token& type2); + void onTypeSpecialization(Token& type, char specialization); + virtual void invalidateGoto(TStatementPtr stmt, GotoError error); virtual void invalidateLabel(TStatementPtr stmt); diff --git a/hphp/compiler/type_annotation.cpp b/hphp/compiler/type_annotation.cpp new file mode 100644 index 000000000..6e4fcb686 --- /dev/null +++ b/hphp/compiler/type_annotation.cpp @@ -0,0 +1,141 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ + +#include +#include + +namespace HPHP { + +/////////////////////////////////////////////////////////////////////////////// +// constructors/destructors + +TypeAnnotation::TypeAnnotation(const std::string &name, + TypeAnnotationPtr typeArgs) : m_name(name), + m_typeArgs(typeArgs), + m_typeList(TypeAnnotationPtr()), + m_nullable(false), + m_soft(false), + m_tuple(false), + m_function(false), + m_xhp(false), + m_typevar(false) {} + +const std::string TypeAnnotation::simpleName() const { + // filter out types that should not be exposed to the runtime + if (m_nullable || m_soft || m_typevar || m_function) { + return ""; + } + if (!m_name.compare("mixed") || !m_name.compare("this")) { + return ""; + } + return m_name; +} + +const std::string TypeAnnotation::fullName() const { + std::string name; + if (m_nullable) { + name += '?'; + } else if (m_soft) { + name += '@'; + } + + if (m_function) { + functionTypeName(name); + } else if (m_xhp) { + xhpTypeName(name); + } else if (m_tuple) { + tupleTypeName(name); + } else if (m_typeArgs) { + genericTypeName(name); + } else { + name += m_name; + } + return name; +} + +void TypeAnnotation::functionTypeName(std::string &name) const { + name += "(function ("; + // return value of function types is the first element of type list + TypeAnnotationPtr retType = m_typeArgs; + TypeAnnotationPtr typeEl = m_typeArgs->m_typeList; + bool hasArgs = typeEl; + while (typeEl) { + name += typeEl->fullName(); + typeEl = typeEl->m_typeList; + name += ", "; + } + // replace the trailing ", " (if any) with "): " + if (hasArgs) { + name.replace(name.size() - 2, 2, "): "); + } else { + name += "): "; + } + // add function return value + name += retType->fullName(); + name += ")"; +} + +// xhp names are mangled so we get them back to their original definition +void TypeAnnotation::xhpTypeName(std::string &name) const { + // remove prefix if any + if (m_name.compare(0, sizeof("xhp_xhp__") - 1, "xhp_xhp__") == 0) { + name += std::string(m_name).replace(0, sizeof("xhp_") - 1, ":"); + } else { + name += m_name; + } + // un-mangle back + Util::replaceAll(name, "__", ":"); + Util::replaceAll(name, "_", "-"); +} + +void TypeAnnotation::tupleTypeName(std::string &name) const { + name += "("; + TypeAnnotationPtr typeEl = m_typeArgs; + while (typeEl) { + name += typeEl->fullName(); + typeEl = typeEl->m_typeList; + name += ", "; + } + // replace the trailing ", " with ")" + name.replace(name.size() - 2, 2, ")"); +} + +void TypeAnnotation::genericTypeName(std::string &name) const { + name += m_name; + name += "<"; + TypeAnnotationPtr typeEl = m_typeArgs; + while (typeEl) { + name += typeEl->fullName(); + typeEl = typeEl->m_typeList; + name += ", "; + } + // replace the trailing ", " with ">" + name.replace(name.size() - 2, 2, ">"); +} + +void TypeAnnotation::appendToTypeList(TypeAnnotationPtr typeList) { + if (m_typeList) { + TypeAnnotationPtr current = m_typeList; + while (current->m_typeList) { + current = current->m_typeList; + } + current->m_typeList = typeList; + } else { + m_typeList = typeList; + } +} + +} diff --git a/hphp/compiler/type_annotation.h b/hphp/compiler/type_annotation.h new file mode 100644 index 000000000..ca6eeaa5c --- /dev/null +++ b/hphp/compiler/type_annotation.h @@ -0,0 +1,102 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ + +#ifndef __TYPE_ANNOTATION_H__ +#define __TYPE_ANNOTATION_H__ + +#include + +namespace HPHP { +/////////////////////////////////////////////////////////////////////////////// + +DECLARE_BOOST_TYPES(TypeAnnotation); + +/** + * A class that represents a type annotation. Type annotations are used + * for arguments in function/method definitions and class fields. + * For simple types (e.g. int, string, C, etc.) m_typeArgs and m_typeList are + * null and the name is all we need. + * For "composite" types the m_typeArgs is set to point to the type list. + * The type list is a an instance of this class with m_typeList pointing to + * the next type (linked list). So for the type + * Map + * the representation would be + * + * __________________ + * | m_name("Map") | __________________ + * | m_typeArgs |--> | m_name("int") | + * | m_typeList(null) | | m_typeArgs(null) | ___________________ + * |__________________| | m_typeList |--> | m_name("string") | + * |__________________| | m_typeArgs(null) | + * | m_typeList(null) | + * |___________________| + * + * Map> is + * + * __________________ + * | m_name("Map") | __________________ + * | m_typeArgs |--> | m_name("int") | + * | m_typeList(null) | | m_typeArgs(null) | ___________________ + * |__________________| | m_typeList |--> | m_name("Vector") | + * |__________________| | m_typeArgs |--- + * | m_typeList(null) | | + * |___________________| | + * | + * | + * __________________ | + * | m_name(string) | <-- + * | m_typeArgs(null) | + * | m_typeList(null) | + * |__________________| + */ +class TypeAnnotation { +public: + TypeAnnotation(const std::string &name, TypeAnnotationPtr typeArgs); + + void setNullable() { m_nullable = true; } + void setSoft() { m_soft = true; } + void setTuple() { m_tuple = true; } + void setFunction() { m_function = true; } + void setXHP() { m_xhp = true; } + void setTypeVar() { m_typevar = true; } + + const std::string simpleName() const; + const std::string fullName() const; + + void appendToTypeList(TypeAnnotationPtr typeList); + +private: + void functionTypeName(std::string &name) const; + void xhpTypeName(std::string &name) const; + void tupleTypeName(std::string &name) const; + void genericTypeName(std::string &name) const; + +private: + std::string m_name; + TypeAnnotationPtr m_typeArgs; + TypeAnnotationPtr m_typeList; + unsigned m_nullable : 1; + unsigned m_soft : 1; + unsigned m_tuple : 1; + unsigned m_function : 1; + unsigned m_xhp : 1; + unsigned m_typevar : 1; +}; + +} + +#endif + diff --git a/hphp/runtime/ext/ext_reflection.cpp b/hphp/runtime/ext/ext_reflection.cpp index 29d554230..783ca3613 100644 --- a/hphp/runtime/ext/ext_reflection.cpp +++ b/hphp/runtime/ext/ext_reflection.cpp @@ -348,8 +348,8 @@ static void set_function_info(Array &ret, const VM::Func* func) { param.set(s_index, VarNR((int)i)); VarNR name(func->localNames()[i]); param.set(s_name, name); - const StringData* type = fpi.typeConstraint().exists() ? - fpi.typeConstraint().typeName() : empty_string.get(); + const StringData* type = fpi.userType() ? + fpi.userType() : empty_string.get(); param.set(s_type, VarNR(type)); param.set(s_function, VarNR(func->name())); if (func->preClass()) { diff --git a/hphp/runtime/vm/func.cpp b/hphp/runtime/vm/func.cpp index ed728cae6..d07ba2890 100644 --- a/hphp/runtime/vm/func.cpp +++ b/hphp/runtime/vm/func.cpp @@ -900,6 +900,7 @@ Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const { pi.setTypeConstraint(m_params[i].typeConstraint()); pi.setUserAttributes(m_params[i].userAttributes()); pi.setBuiltinType(m_params[i].builtinType()); + pi.setUserType(m_params[i].userType()); f->appendParam(m_params[i].ref(), pi, pBuilder); } f->shared()->m_params = pBuilder; diff --git a/hphp/runtime/vm/func.h b/hphp/runtime/vm/func.h index 58e093990..dbfe81c32 100644 --- a/hphp/runtime/vm/func.h +++ b/hphp/runtime/vm/func.h @@ -43,7 +43,7 @@ struct Func { // construct a dummy ParamInfo ParamInfo() : m_builtinType(KindOfInvalid), m_funcletOff(InvalidAbsoluteOffset), - m_phpCode(nullptr) { + m_phpCode(nullptr), m_userType(nullptr) { tvWriteUninit(&m_defVal); } @@ -59,6 +59,7 @@ struct Func { (tcName) (tcNullable) (m_userAttributes) + (m_userType) ; if (SerDe::deserializing) { @@ -100,6 +101,12 @@ struct Func { const Func::UserAttributeMap& userAttributes() const { return m_userAttributes; } + void setUserType(const StringData* userType) { + m_userType = userType; + } + const StringData* userType() const { + return m_userType; + } private: DataType m_builtinType; // typehint for builtins @@ -110,6 +117,8 @@ struct Func { TypeConstraint m_typeConstraint; Func::UserAttributeMap m_userAttributes; + // the type the user typed in source code, contains type parameters and all + const StringData* m_userType; }; struct SVInfo { // Static variable info. const StringData* name; diff --git a/hphp/test/slow/reflection_classes/1363.php.expect b/hphp/test/slow/reflection_classes/1363.php.expect index 87faddf9d..3de42dd2d 100644 --- a/hphp/test/slow/reflection_classes/1363.php.expect +++ b/hphp/test/slow/reflection_classes/1363.php.expect @@ -1,7 +1,7 @@ -string(6) "Vector" +string(11) "Vector" string(3) "int" bool(false) string(6) "string" string(6) "string" -bool(false) +string(1) "T" string(1) "C" diff --git a/hphp/test/test_code_run.cpp b/hphp/test/test_code_run.cpp index b51ea628f..b0d23b7f4 100644 --- a/hphp/test/test_code_run.cpp +++ b/hphp/test/test_code_run.cpp @@ -13833,12 +13833,12 @@ bool TestCodeRun::TestReflectionClasses() { "$rm = $rc->getMethod('t'); " "var_dump($rm->getReturnTypehintText());" , - "string(6) \"Vector\"\n" + "string(11) \"Vector\"\n" "string(3) \"int\"\n" "bool(false)\n" "string(6) \"string\"\n" "string(6) \"string\"\n" - "bool(false)\n" + "string(1) \"T\"\n" "string(1) \"C\"\n"); // field type constraints (hints) @@ -13902,6 +13902,185 @@ bool TestCodeRun::TestReflectionClasses() { "string(6) \"string\"\n" "string(2) \"C2\"\n"); + // general type annotation tests + MVCRO("getParameters();" + " foreach($rps as $rp) {" + " var_dump($rp->getTypehintText());" + " }" + " var_dump($rf->getReturnTypehintText());" + "}" + "function printClass($rc) {" + " $rms = $rc->getMethods();" + " $meths = array();" + " foreach($rms as $rm) {" + " $meths[$rm->getName()] = $rm;" + " }" + " ksort($meths);" + " foreach($meths as $meth) {" + " printFunc($meth);" + " }" + " $rps = $rc->getProperties();" + " $props = array();" + " foreach($rps as $rp) {" + " $props[$rp->getName()] = $rp;" + " }" + " ksort($props);" + " foreach($props as $prop) {" + " var_dump($prop->getTypehintText());" + " }" + "}" + "function f() {}" + "$rf = new ReflectionFunction('f');" + "printFunc($rf);" + "function f1(int $t) {}" + "$rf = new ReflectionFunction('f1');" + "printFunc($rf);" + "function f2(@string $s) {}" + "$rf = new ReflectionFunction('f2');" + "printFunc($rf);" + "function f3(?:xhp:hello $x) {}" + "$rf = new ReflectionFunction('f3');" + "printFunc($rf);" + "function f100(): int {}" + "$rf = new ReflectionFunction('f100');" + "printFunc($rf);" + "function f101(): string {}" + "$rf = new ReflectionFunction('f101');" + "printFunc($rf);" + "function f102(): Vector<:xhp:element> {}" + "$rf = new ReflectionFunction('f102');" + "printFunc($rf);" + "function f200((string, Template, ?int, Vector>) $tuple): ?int {}" + "$rf = new ReflectionFunction('f200');" + "printFunc($rf);" + "function f201((function (@int, Map>>):Vector) $i): ClassA {}" + "$rf = new ReflectionFunction('f201');" + "printFunc($rf);" + "function f202(:xhp:html-element $html): Map> {}" + "$rf = new ReflectionFunction('f202');" + "printFunc($rf);" + "function f203((int, Vector) $tupple): array {}" + "$rf = new ReflectionFunction('f203');" + "printFunc($rf);" + "function f204((function (int): Vector) $f): array {}" + "$rf = new ReflectionFunction('f204');" + "printFunc($rf);" + "function f300(Y $y, ?double $d): X {}" + "$rf = new ReflectionFunction('f300');" + "printFunc($rf);" + "function f301((function (): Vector) $f): array {}" + "$rf = new ReflectionFunction('f301');" + "printFunc($rf);" + "function f302((Y, X, double, string) $f): ?Y {}" + "$rf = new ReflectionFunction('f302');" + "printFunc($rf);" + "class C {" + " public @int $a;" + " public ?string $b;" + " public Vector $v;" + " public (string, (function(?int, (string, string)):void)) $c;" + " public function __construct(?int $i, (int, string) $c) {}" + " static public function m1() {}" + " public function m2(@double $d) : void {}" + " static public function m3(Vector>> $v, :xhp:html $x) : @array> {}" + " public function m4((function(@int, (string, string)): void) $v) : array> {}" + "}" + "$rc = new ReflectionClass('C');" + "printClass($rc);" + "class CT {" + " public @int $a;" + " public ?string $b;" + " public Vector $v;" + " public (string, (function(?int, (X, Y)):void)) $c;" + " public function __construct(?int $i, (int, X) $c) {}" + " static public function m1() {}" + " public function m2(@Y $d) : void {}" + " static public function m3(Vector>> $v, :xhp:html $x) : @array> {}" + " public function m4((function(@int, (X, string)): void) $v) : array> {}" + "}" + "$rc = new ReflectionClass('CT');" + "printClass($rc);" + "trait T {" + " static public function m1() {}" + " public function m2(@int $d) : void {}" + " static public function m3(Vector>> $v, :xhp:html $x) : @array> {}" + " public function m4((function(@int, (double, string)): void) $v) : array, :xhp:html>> {}" + "}" + "class TC {" + " use T;" + "}" + "$rc = new ReflectionClass('TC');" + "printClass($rc);" + , + "bool(false)\n" + "string(3) \"int\"\n" + "bool(false)\n" + "string(7) \"@string\"\n" + "bool(false)\n" + "string(11) \"?:xhp:hello\"\n" + "bool(false)\n" + "string(3) \"int\"\n" + "string(6) \"string\"\n" + "string(20) \"Vector<:xhp:element>\"\n" + "string(53) \"(string, Template, ?int, Vector>)\"\n" + "string(4) \"?int\"\n" + "string(67) \"(function (@int, Map>>): Vector)\"\n" + "string(6) \"ClassA\"\n" + "string(17) \":xhp:html-element\"\n" + "string(38) \"Map>\"\n" + "string(21) \"(int, Vector)\"\n" + "string(13) \"array\"\n" + "string(32) \"(function (int): Vector)\"\n" + "string(21) \"array\"\n" + "string(1) \"Y\"\n" + "string(7) \"?double\"\n" + "string(1) \"X\"\n" + "string(24) \"(function (): Vector)\"\n" + "string(16) \"array\"\n" + "string(22) \"(Y, X, double, string)\"\n" + "string(2) \"?Y\"\n" + "string(4) \"?int\"\n" + "string(13) \"(int, string)\"\n" + "bool(false)\n" + "bool(false)\n" + "string(7) \"@double\"\n" + "string(4) \"void\"\n" + "string(33) \"Vector>>\"\n" + "string(9) \":xhp:html\"\n" + "string(28) \"@array>\"\n" + "string(41) \"(function (@int, (string, string)): void)\"\n" + "string(29) \"array>\"\n" + "string(4) \"@int\"\n" + "string(7) \"?string\"\n" + "string(51) \"(string, (function (?int, (string, string)): void))\"\n" + "string(9) \"Vector\"\n" + "string(4) \"?int\"\n" + "string(8) \"(int, X)\"\n" + "bool(false)\n" + "bool(false)\n" + "string(2) \"@Y\"\n" + "string(4) \"void\"\n" + "string(28) \"Vector>>\"\n" + "string(9) \":xhp:html\"\n" + "string(26) \"@array>\"\n" + "string(36) \"(function (@int, (X, string)): void)\"\n" + "string(24) \"array>\"\n" + "string(4) \"@int\"\n" + "string(7) \"?string\"\n" + "string(41) \"(string, (function (?int, (X, Y)): void))\"\n" + "string(9) \"Vector\"\n" + "bool(false)\n" + "string(4) \"@int\"\n" + "string(4) \"void\"\n" + "string(28) \"Vector>>\"\n" + "string(9) \":xhp:html\"\n" + "string(26) \"@array>\"\n" + "string(41) \"(function (@int, (double, string)): void)\"\n" + "string(38) \"array, :xhp:html>>\"\n" + ); + { HipHopSyntax w(this); MVCROF( diff --git a/hphp/util/parser/hphp.y b/hphp/util/parser/hphp.y index 63d2da42c..d51204f9f 100644 --- a/hphp/util/parser/hphp.y +++ b/hphp/util/parser/hphp.y @@ -2352,13 +2352,16 @@ sm_typeargs_opt: ; sm_type_list: - sm_type - | sm_type_list ',' sm_type + sm_type { Token t; t.reset(); + _p->onTypeList($1, t); + $$ = $1; } + | sm_type_list ',' sm_type { _p->onTypeList($1, $3); + $$ = $1; } ; sm_func_type_list: - sm_type_list ',' T_VARARG { $$.reset(); } - | sm_type_list { $$.reset(); } + sm_type_list ',' T_VARARG { $$ = $1; } + | sm_type_list { $$ = $1; } | T_VARARG { $$.reset(); } | { $$.reset(); } ; @@ -2407,33 +2410,41 @@ sm_shape_type: sm_type: /* double-optional types will be rejected by the typechecker; we * already allow plenty of nonsense types anyway */ - '?' sm_type { only_in_strict_mode(_p); $$.reset(); } - | '@' sm_type { only_in_strict_mode(_p); $$.reset(); } - | ident sm_typeargs_opt { $$ = $1; - /* if the type annotation is a bound - typevar we have to strip it */ - if (_p->scanner().isStrictMode() && - (_p->isTypeVar($$.text()) || - !$$.text().compare("mixed") || - !$$.text().compare("this") - )) { - $$.reset(); - } - } - | T_ARRAY { $$.setText("array"); } + '?' sm_type { only_in_strict_mode(_p); + _p->onTypeSpecialization($2, '?'); + $$ = $2; } + | '@' sm_type { only_in_strict_mode(_p); + _p->onTypeSpecialization($2, '@'); + $$ = $2; } + | ident sm_typeargs_opt { _p->onTypeAnnotation($$, $1, $2); } + | T_ARRAY { Token t; t.reset(); + $1.setText("array"); + _p->onTypeAnnotation($$, $1, t); } | sm_shape_type { $$ = $1; } | T_ARRAY T_TYPELIST_LT sm_type T_TYPELIST_GT { only_in_strict_mode(_p); - $$.setText("array"); } + $1.setText("array"); + _p->onTypeAnnotation($$, $1, $3); } | T_ARRAY T_TYPELIST_LT sm_type ',' sm_type T_TYPELIST_GT { only_in_strict_mode(_p); - $$.setText("array"); } - | T_XHP_LABEL { $1.xhpLabel(); $$ = $1; } + _p->onTypeList($3, $5); + $1.setText("array"); + _p->onTypeAnnotation($$, $1, $3); } + | T_XHP_LABEL { $1.xhpLabel(); + Token t; t.reset(); + _p->onTypeAnnotation($$, $1, t); + _p->onTypeSpecialization($$, 'x'); } | '(' T_FUNCTION '(' sm_func_type_list ')' - ':' sm_type ')' { only_in_strict_mode(_p); $$.reset(); } + ':' sm_type ')' { only_in_strict_mode(_p); + _p->onTypeList($7, $4); + _p->onTypeAnnotation($$, $2, $7); + _p->onTypeSpecialization($$, 'f'); } | '(' sm_type_list ',' sm_type ')' { only_in_strict_mode(_p); - $$.setText("array"); } + _p->onTypeList($2, $4); + Token t; t.reset(); t.setText("array"); + _p->onTypeAnnotation($$, t, $2); + _p->onTypeSpecialization($$, 't'); } ; sm_type_opt: diff --git a/hphp/util/parser/test/hphp.tab.cpp b/hphp/util/parser/test/hphp.tab.cpp index cb2a9c554..92dce225d 100644 --- a/hphp/util/parser/test/hphp.tab.cpp +++ b/hphp/util/parser/test/hphp.tab.cpp @@ -1622,10 +1622,10 @@ static const yytype_uint16 yyrline[] = 2261, 2264, 2267, 2269, 2271, 2275, 2276, 2278, 2279, 2285, 2286, 2288, 2290, 2292, 2294, 2297, 2298, 2299, 2303, 2304, 2305, 2306, 2307, 2308, 2309, 2313, 2314, 2318, 2327, 2332, - 2333, 2339, 2340, 2348, 2351, 2355, 2356, 2360, 2361, 2362, - 2363, 2367, 2368, 2372, 2373, 2374, 2376, 2378, 2379, 2383, - 2389, 2391, 2395, 2398, 2401, 2410, 2411, 2412, 2423, 2424, - 2425, 2428, 2431, 2432, 2435, 2440, 2441 + 2333, 2339, 2340, 2348, 2351, 2355, 2358, 2363, 2364, 2365, + 2366, 2370, 2371, 2375, 2376, 2377, 2379, 2381, 2382, 2386, + 2392, 2394, 2398, 2401, 2404, 2413, 2416, 2419, 2420, 2423, + 2424, 2428, 2433, 2437, 2443, 2451, 2452 }; #endif @@ -10168,101 +10168,118 @@ yyreduce: { (yyval).reset(); ;} break; + case 675: + +/* Line 1455 of yacc.c */ +#line 2355 "../../../../hphp/util/parser/hphp.y" + { Token t; t.reset(); + _p->onTypeList((yyvsp[(1) - (1)]), t); + (yyval) = (yyvsp[(1) - (1)]); ;} + break; + + case 676: + +/* Line 1455 of yacc.c */ +#line 2358 "../../../../hphp/util/parser/hphp.y" + { _p->onTypeList((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); + (yyval) = (yyvsp[(1) - (3)]); ;} + break; + case 677: /* Line 1455 of yacc.c */ -#line 2360 "../../../../hphp/util/parser/hphp.y" - { (yyval).reset(); ;} +#line 2363 "../../../../hphp/util/parser/hphp.y" + { (yyval) = (yyvsp[(1) - (3)]); ;} break; case 678: /* Line 1455 of yacc.c */ -#line 2361 "../../../../hphp/util/parser/hphp.y" - { (yyval).reset(); ;} +#line 2364 "../../../../hphp/util/parser/hphp.y" + { (yyval) = (yyvsp[(1) - (1)]); ;} break; case 679: /* Line 1455 of yacc.c */ -#line 2362 "../../../../hphp/util/parser/hphp.y" +#line 2365 "../../../../hphp/util/parser/hphp.y" { (yyval).reset(); ;} break; case 680: /* Line 1455 of yacc.c */ -#line 2363 "../../../../hphp/util/parser/hphp.y" +#line 2366 "../../../../hphp/util/parser/hphp.y" { (yyval).reset(); ;} break; case 681: /* Line 1455 of yacc.c */ -#line 2367 "../../../../hphp/util/parser/hphp.y" +#line 2370 "../../../../hphp/util/parser/hphp.y" { (yyval).reset(); ;} break; case 682: /* Line 1455 of yacc.c */ -#line 2368 "../../../../hphp/util/parser/hphp.y" +#line 2371 "../../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); (yyval) = (yyvsp[(2) - (2)]); ;} break; case 683: /* Line 1455 of yacc.c */ -#line 2372 "../../../../hphp/util/parser/hphp.y" +#line 2375 "../../../../hphp/util/parser/hphp.y" { _p->addTypeVar((yyvsp[(1) - (3)]).text()); ;} break; case 684: /* Line 1455 of yacc.c */ -#line 2373 "../../../../hphp/util/parser/hphp.y" +#line 2376 "../../../../hphp/util/parser/hphp.y" { _p->addTypeVar((yyvsp[(1) - (1)]).text()); ;} break; case 686: /* Line 1455 of yacc.c */ -#line 2377 "../../../../hphp/util/parser/hphp.y" +#line 2380 "../../../../hphp/util/parser/hphp.y" { _p->addTypeVar((yyvsp[(1) - (5)]).text()); ;} break; case 687: /* Line 1455 of yacc.c */ -#line 2378 "../../../../hphp/util/parser/hphp.y" +#line 2381 "../../../../hphp/util/parser/hphp.y" { _p->addTypeVar((yyvsp[(1) - (3)]).text()); ;} break; case 689: /* Line 1455 of yacc.c */ -#line 2385 "../../../../hphp/util/parser/hphp.y" +#line 2388 "../../../../hphp/util/parser/hphp.y" { validate_shape_keyname((yyvsp[(1) - (3)]), _p); ;} break; case 692: /* Line 1455 of yacc.c */ -#line 2396 "../../../../hphp/util/parser/hphp.y" +#line 2399 "../../../../hphp/util/parser/hphp.y" { (yyval) = (yyvsp[(1) - (2)]); ;} break; case 693: /* Line 1455 of yacc.c */ -#line 2398 "../../../../hphp/util/parser/hphp.y" +#line 2401 "../../../../hphp/util/parser/hphp.y" {;} break; case 694: /* Line 1455 of yacc.c */ -#line 2402 "../../../../hphp/util/parser/hphp.y" +#line 2405 "../../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); (yyval).setText("array"); ;} break; @@ -10270,54 +10287,51 @@ yyreduce: case 695: /* Line 1455 of yacc.c */ -#line 2410 "../../../../hphp/util/parser/hphp.y" - { only_in_strict_mode(_p); (yyval).reset(); ;} +#line 2413 "../../../../hphp/util/parser/hphp.y" + { only_in_strict_mode(_p); + _p->onTypeSpecialization((yyvsp[(2) - (2)]), '?'); + (yyval) = (yyvsp[(2) - (2)]); ;} break; case 696: /* Line 1455 of yacc.c */ -#line 2411 "../../../../hphp/util/parser/hphp.y" - { only_in_strict_mode(_p); (yyval).reset(); ;} +#line 2416 "../../../../hphp/util/parser/hphp.y" + { only_in_strict_mode(_p); + _p->onTypeSpecialization((yyvsp[(2) - (2)]), '@'); + (yyval) = (yyvsp[(2) - (2)]); ;} break; case 697: /* Line 1455 of yacc.c */ -#line 2412 "../../../../hphp/util/parser/hphp.y" - { (yyval) = (yyvsp[(1) - (2)]); - /* if the type annotation is a bound - typevar we have to strip it */ - if (_p->scanner().isStrictMode() && - (_p->isTypeVar((yyval).text()) || - !(yyval).text().compare("mixed") || - !(yyval).text().compare("this") - )) { - (yyval).reset(); - } - ;} +#line 2419 "../../../../hphp/util/parser/hphp.y" + { _p->onTypeAnnotation((yyval), (yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); ;} break; case 698: /* Line 1455 of yacc.c */ -#line 2423 "../../../../hphp/util/parser/hphp.y" - { (yyval).setText("array"); ;} +#line 2420 "../../../../hphp/util/parser/hphp.y" + { Token t; t.reset(); + (yyvsp[(1) - (1)]).setText("array"); + _p->onTypeAnnotation((yyval), (yyvsp[(1) - (1)]), t); ;} break; case 699: /* Line 1455 of yacc.c */ -#line 2424 "../../../../hphp/util/parser/hphp.y" +#line 2423 "../../../../hphp/util/parser/hphp.y" { (yyval) = (yyvsp[(1) - (1)]); ;} break; case 700: /* Line 1455 of yacc.c */ -#line 2426 "../../../../hphp/util/parser/hphp.y" +#line 2425 "../../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); - (yyval).setText("array"); ;} + (yyvsp[(1) - (4)]).setText("array"); + _p->onTypeAnnotation((yyval), (yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); ;} break; case 701: @@ -10325,49 +10339,60 @@ yyreduce: /* Line 1455 of yacc.c */ #line 2429 "../../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); - (yyval).setText("array"); ;} + _p->onTypeList((yyvsp[(3) - (6)]), (yyvsp[(5) - (6)])); + (yyvsp[(1) - (6)]).setText("array"); + _p->onTypeAnnotation((yyval), (yyvsp[(1) - (6)]), (yyvsp[(3) - (6)])); ;} break; case 702: /* Line 1455 of yacc.c */ -#line 2431 "../../../../hphp/util/parser/hphp.y" - { (yyvsp[(1) - (1)]).xhpLabel(); (yyval) = (yyvsp[(1) - (1)]); ;} +#line 2433 "../../../../hphp/util/parser/hphp.y" + { (yyvsp[(1) - (1)]).xhpLabel(); + Token t; t.reset(); + _p->onTypeAnnotation((yyval), (yyvsp[(1) - (1)]), t); + _p->onTypeSpecialization((yyval), 'x'); ;} break; case 703: /* Line 1455 of yacc.c */ -#line 2434 "../../../../hphp/util/parser/hphp.y" - { only_in_strict_mode(_p); (yyval).reset(); ;} +#line 2439 "../../../../hphp/util/parser/hphp.y" + { only_in_strict_mode(_p); + _p->onTypeList((yyvsp[(7) - (8)]), (yyvsp[(4) - (8)])); + _p->onTypeAnnotation((yyval), (yyvsp[(2) - (8)]), (yyvsp[(7) - (8)])); + _p->onTypeSpecialization((yyval), 'f'); ;} break; case 704: /* Line 1455 of yacc.c */ -#line 2435 "../../../../hphp/util/parser/hphp.y" +#line 2443 "../../../../hphp/util/parser/hphp.y" { only_in_strict_mode(_p); - (yyval).setText("array"); ;} + _p->onTypeList((yyvsp[(2) - (5)]), (yyvsp[(4) - (5)])); + Token t; t.reset(); t.setText("array"); + _p->onTypeAnnotation((yyval), t, (yyvsp[(2) - (5)])); + _p->onTypeSpecialization((yyval), 's'); ;} break; case 705: /* Line 1455 of yacc.c */ -#line 2440 "../../../../hphp/util/parser/hphp.y" +#line 2451 "../../../../hphp/util/parser/hphp.y" { (yyval) = (yyvsp[(1) - (1)]); ;} break; case 706: /* Line 1455 of yacc.c */ -#line 2441 "../../../../hphp/util/parser/hphp.y" +#line 2452 "../../../../hphp/util/parser/hphp.y" { (yyval).reset(); ;} break; /* Line 1455 of yacc.c */ -#line 10374 "hphp.tab.cpp" +#line 10399 "hphp.tab.cpp" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); @@ -10587,7 +10612,7 @@ yyreturn: /* Line 1675 of yacc.c */ -#line 2444 "../../../../hphp/util/parser/hphp.y" +#line 2455 "../../../../hphp/util/parser/hphp.y" bool Parser::parseImpl() { return yyparse(this) == 0; diff --git a/hphp/util/parser/test/parser.h b/hphp/util/parser/test/parser.h index 8e4c607e6..adcb09b07 100644 --- a/hphp/util/parser/test/parser.h +++ b/hphp/util/parser/test/parser.h @@ -499,6 +499,18 @@ struct Parser : ParserBase { void onTypedef(Token& out, Token& name, Token& value) { X(out, name, value); } + + void onTypeAnnotation(Token& out, const Token& name, const Token& typeArgs) { + X(out, name, typeArgs); + } + + void onTypeList(Token& type1, const Token& type2) { + X(type1, type2); + } + + void onTypeSpecialization(const Token& type, char specialization) { + X(type, specialization); + } }; //////////////////////////////////////////////////////////////////////