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); + } }; //////////////////////////////////////////////////////////////////////