Skip to content

Commit

Permalink
Improve pseudo-constant support in the typechecker
Browse files Browse the repository at this point in the history
Summary:
These pseudo-constants are always considered to be global (even in namespaces), because they compile to literals or special bytecodes.

This enforces that the pseudo-constant names are reserved and that we do not elaborate them, regardless of whether they're in a namespace.

Reviewed By: Wilfred

Differential Revision: D10204718

fbshipit-source-id: 73641478ef74ea0a6d5dd8da6d74a1aa9b397751
  • Loading branch information
kmeht authored and hhvm-bot committed Oct 14, 2018
1 parent d3f7d20 commit 84c4be5
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 47 deletions.
56 changes: 11 additions & 45 deletions hphp/hack/src/naming/naming.ml
Expand Up @@ -493,44 +493,6 @@ end = struct
let get_name genv get_pos x =
lookup genv get_pos x; x

(* For dealing with namespace fallback on constants *)
let elaborate_and_get_name_with_fallback
mk_dep
genv
(get_pos : string -> FileInfo.pos option) x =
let get_name x = get_name genv get_pos x in
let fq_x = NS.elaborate_id genv.namespace NS.ElaborateConst x in
let need_fallback =
genv.namespace.Namespace_env.ns_name <> None &&
not (String.contains (snd x) '\\') in
let use_fallback =
need_fallback &&
(* __FILE__, __LINE__ etc *)
(string_starts_with (snd x) "__") && (string_ends_with (snd x) "__") in
if use_fallback then begin
let global_x = (fst x, "\\" ^ (snd x)) in
(* Explicitly add dependencies on both of the consts we could be
* referring to here. Normally naming doesn't have to deal with
* deps at all -- they are added during typechecking just by the
* nature of looking up a class or function name. However, we're
* flattening namespaces here, and the fallback behavior of
* consts means that we might suddenly be referring to a
* different const without any change to the callsite at
* all. Adding both dependencies explicitly captures this
* action-at-a-distance. *)
Typing_deps.add_idep genv.droot (mk_dep (snd fq_x));
Typing_deps.add_idep genv.droot (mk_dep (snd global_x));
let mem (_, s) = get_pos s in
match mem fq_x, mem global_x with
(* Found in the current namespace *)
| Some _, _ -> get_name fq_x
(* Found in the global namespace *)
| _, Some _ -> get_name global_x
(* Not found. Pick the more specific one to error on. *)
| None, None -> get_name fq_x
end else
get_name fq_x

(* For dealing with namespace resolution on functions *)
let elaborate_and_get_name_with_canonicalized_fallback
genv
Expand All @@ -543,13 +505,9 @@ end = struct
let fq_x = canonicalize fq_x `func in
get_name fq_x

let global_const (genv, _env) x =
elaborate_and_get_name_with_fallback
(* Same idea as Dep.FunName, see below. *)
(fun x -> Typing_deps.Dep.GConstName x)
genv
(Naming_heap.ConstPosHeap.get)
x
let global_const (genv, _env) x =
let fq_x = NS.elaborate_id genv.namespace NS.ElaborateConst x in
get_name genv (Naming_heap.ConstPosHeap.get) fq_x

let type_name (genv, _) x ~allow_typedef =
(* Generic names are not allowed to shadow class names *)
Expand Down Expand Up @@ -2916,11 +2874,19 @@ module Make (GetLocals : GetLocals) = struct
| None
| Some _ -> ()

let check_constant_name genv cst =
if genv.namespace.Namespace_env.ns_name <> None then
let pos, name = cst.cst_name in
let name = Utils.strip_all_ns name in
if SN.PseudoConsts.is_pseudo_const (Utils.add_ns name) then
Errors.name_is_reserved name pos

let global_const genv cst =
let env = Env.make_const_env genv cst in
let hint = Option.map cst.cst_type (hint env) in
let e = match cst.cst_kind with
| Ast.Cst_const ->
check_constant_name (fst env) cst;
check_constant_hint cst;
Some (constant_expr env cst.cst_value)
(* Define allows any expression, so don't call check_constant.
Expand Down
10 changes: 8 additions & 2 deletions hphp/hack/src/parser/namespaces.ml
Expand Up @@ -11,6 +11,8 @@ open Core_kernel
open Ast
open Namespace_env

module SN = Naming_special_names

(* When dealing with an <?hh file, HHVM automatically imports a few
* "core" classes into every namespace, mostly collections. Their
* unqualified names always refer to this global version.
Expand Down Expand Up @@ -117,10 +119,10 @@ let autoimport_set =
* Return false as first value if it is not auto imported
*)
let get_autoimport_name_namespace id =
if Naming_special_names.Typehints.is_reserved_global_name id
if SN.Typehints.is_reserved_global_name id
then (true, None)
else
if Naming_special_names.Typehints.is_reserved_hh_name id ||
if SN.Typehints.is_reserved_hh_name id ||
SSet.mem id autoimport_set
then (true, Some "HH")
else (false, None)
Expand Down Expand Up @@ -204,6 +206,10 @@ let elaborate_id_impl ~autoimport nsenv kind (p, id) =
else
let was_renamed, fully_qualified =
begin
let global_id = Utils.add_ns id in
if kind = ElaborateConst && SN.PseudoConsts.is_pseudo_const global_id
then false, global_id
else
(* Expand "use" imports. *)
let (bslash_loc, has_bslash) =
match String.index id '\\' with
Expand Down
6 changes: 6 additions & 0 deletions hphp/hack/src/utils/utils.ml
Expand Up @@ -148,6 +148,12 @@ let strip_ns s =
if String.length s == 0 || s.[0] <> '\\' then s
else String.sub s 1 ((String.length s) - 1)

(* A\B\C -> \A\B\C *)
let add_ns s =
if String.length s = 0 || s.[0] <> '\\'
then "\\" ^ s
else s

(* \A\B\C -> C *)
let strip_all_ns s =
try
Expand Down
5 changes: 5 additions & 0 deletions hphp/hack/test/typecheck/reserved_name4.php
@@ -0,0 +1,5 @@
<?hh

namespace X {
const __FILE__ = 1;
}
2 changes: 2 additions & 0 deletions hphp/hack/test/typecheck/reserved_name4.php.exp
@@ -0,0 +1,2 @@
File "reserved_name4.php", line 4, characters 9-16:
__FILE__ cannot be used as it is reserved. (Naming[2068])

0 comments on commit 84c4be5

Please sign in to comment.