Skip to content

Commit 87d2735

Browse files
vassilmladenovfacebook-github-bot
authored andcommitted
Fix bug with synthesized typeconst inheritance
Summary: This is a follow up from D37656012 (e555f82). For classes (not traits), we say that a synthesized member does not replace a synthesized member when folding a class, even when the trait carries a more specific type. Otherwise, we have a bug where `Parentt` in the example below thinks that T is synthesized, so Child does not report an inheritance conflict from its trait. Reviewed By: francesco-zappa-nardelli Differential Revision: D39158782 fbshipit-source-id: 8aead0388d6552c02776db3263f8c8795f5f0c12
1 parent 19b83cc commit 87d2735

File tree

9 files changed

+95
-45
lines changed

9 files changed

+95
-45
lines changed

hphp/hack/src/compare_folded_decls_file.ml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ let popt
1515
~disable_xhp_element_mangling
1616
~disable_enum_classes
1717
~interpret_soft_types_as_like_types
18-
~everything_sdt =
18+
~everything_sdt
19+
~enable_strict_const_semantics =
1920
let enable_enum_classes = not disable_enum_classes in
2021
let po = ParserOptions.default in
2122
let po =
@@ -34,6 +35,13 @@ let popt
3435
interpret_soft_types_as_like_types
3536
in
3637
let po = ParserOptions.with_everything_sdt po everything_sdt in
38+
let po =
39+
{
40+
po with
41+
GlobalOptions.tco_enable_strict_const_semantics =
42+
enable_strict_const_semantics;
43+
}
44+
in
3745
po
3846

3947
let init ~enable_strict_const_semantics popt : Provider_context.t =
@@ -260,6 +268,7 @@ let () =
260268
~disable_enum_classes
261269
~interpret_soft_types_as_like_types
262270
~everything_sdt
271+
~enable_strict_const_semantics
263272
in
264273
let tco_experimental_features =
265274
TypecheckerOptions.experimental_from_flags

hphp/hack/src/decl/decl_inherit.ml

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,10 @@ let add_const name const acc =
197197

198198
let add_members members acc = SMap.fold SMap.add members acc
199199

200-
let add_typeconst ~strict_const_semantics name sig_ typeconsts =
200+
let add_typeconst env c name sig_ typeconsts =
201+
let fix_synthesized =
202+
TypecheckerOptions.enable_strict_const_semantics (Decl_env.tcopt env) > 3
203+
in
201204
match SMap.find_opt name typeconsts with
202205
| None ->
203206
(* The type constant didn't exist so far, let's add it *)
@@ -221,7 +224,8 @@ let add_typeconst ~strict_const_semantics name sig_ typeconsts =
221224
old_sig.ttc_kind,
222225
sig_.ttc_kind )
223226
with
224-
| (false, true, _, _) when strict_const_semantics ->
227+
| (false, true, _, _) when Ast_defs.is_c_class c.sc_kind && fix_synthesized
228+
->
225229
(* Don't replace a type constant with a synthesized type constant. This
226230
covers the following case:
227231
@@ -296,7 +300,7 @@ let add_constructor (cstr, cstr_consist) (acc, acc_consist) =
296300
in
297301
(ce, Decl_utils.coalesce_consistent acc_consist cstr_consist)
298302

299-
let add_inherited _env inherited acc =
303+
let add_inherited env c inherited acc =
300304
{
301305
ih_substs =
302306
SMap.merge
@@ -331,18 +335,7 @@ let add_inherited _env inherited acc =
331335
ih_cstr = add_constructor inherited.ih_cstr acc.ih_cstr;
332336
ih_consts = SMap.fold add_const inherited.ih_consts acc.ih_consts;
333337
ih_typeconsts =
334-
begin
335-
(* TODO(T125402906) Re-enable this after fixing inheritance *)
336-
let strict_const_semantics =
337-
false
338-
(* TypecheckerOptions.enable_strict_const_semantics (Decl_env.tcopt env)
339-
> 2 *)
340-
in
341-
SMap.fold
342-
(add_typeconst ~strict_const_semantics)
343-
inherited.ih_typeconsts
344-
acc.ih_typeconsts
345-
end;
338+
SMap.fold (add_typeconst env c) inherited.ih_typeconsts acc.ih_typeconsts;
346339
ih_props = add_members inherited.ih_props acc.ih_props;
347340
ih_sprops = add_members inherited.ih_sprops acc.ih_sprops;
348341
ih_methods = add_methods inherited.ih_methods acc.ih_methods;
@@ -617,25 +610,26 @@ let parents_which_provide_members c =
617610

618611
let from_parent env c (parents : Decl_store.class_entries SMap.t) acc parent =
619612
let inherited = from_class env c parents parent in
620-
add_inherited env inherited acc
613+
add_inherited env c inherited acc
621614

622615
let from_requirements env c parents acc reqs =
623616
let inherited = from_class env c parents reqs in
624617
let inherited = mark_as_synthesized inherited in
625-
add_inherited env inherited acc
618+
add_inherited env c inherited acc
626619

627620
let from_trait env c parents acc uses =
628621
let inherited = from_class env c parents uses in
629-
add_inherited env inherited acc
622+
add_inherited env c inherited acc
630623

631-
let from_xhp_attr_use env (parents : Decl_store.class_entries SMap.t) acc uses =
624+
let from_xhp_attr_use env c (parents : Decl_store.class_entries SMap.t) acc uses
625+
=
632626
let inherited = from_class_xhp_attrs_only env parents uses in
633-
add_inherited env inherited acc
627+
add_inherited env c inherited acc
634628

635629
let from_interface_constants
636-
env (parents : Decl_store.class_entries SMap.t) acc impls =
630+
env c (parents : Decl_store.class_entries SMap.t) acc impls =
637631
let inherited = from_class_constants_only env parents impls in
638-
add_inherited env inherited acc
632+
add_inherited env c inherited acc
639633

640634
let has_highest_precedence : (OverridePrecedence.t * 'a) option -> bool =
641635
function
@@ -737,7 +731,7 @@ let make env c ~cache:(parents : Decl_store.class_entries SMap.t) =
737731
in
738732
let acc =
739733
List.fold_left
740-
~f:(from_xhp_attr_use env parents)
734+
~f:(from_xhp_attr_use env c parents)
741735
~init:acc
742736
c.sc_xhp_attr_uses
743737
in
@@ -748,7 +742,7 @@ let make env c ~cache:(parents : Decl_store.class_entries SMap.t) =
748742
*)
749743
let acc =
750744
List.fold_left
751-
~f:(from_interface_constants env parents)
745+
~f:(from_interface_constants env c parents)
752746
~init:acc
753747
c.sc_req_implements
754748
in
@@ -759,12 +753,12 @@ let make env c ~cache:(parents : Decl_store.class_entries SMap.t) =
759753
in
760754
let acc =
761755
List.fold_left
762-
~f:(from_interface_constants env parents)
756+
~f:(from_interface_constants env c parents)
763757
~init:acc
764758
included_enums
765759
in
766760
List.fold_left
767-
~f:(from_interface_constants env parents)
761+
~f:(from_interface_constants env c parents)
768762
~init:acc
769763
c.sc_implements
770764

hphp/hack/src/hackrs/decl_parser/decl_parser.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use ty::reason::Reason;
2323
#[derive(Debug, Clone)]
2424
pub struct DeclParser<R: Reason> {
2525
file_provider: Arc<dyn FileProvider>,
26-
opts: ParserOptions,
26+
pub opts: ParserOptions,
2727
// We could make our parse methods generic over `R` instead, but it's
2828
// usually more convenient for callers (especially tests) to pin the decl
2929
// parser to a single Reason type.

hphp/hack/src/hackrs/folded_decl_provider/inherit.rs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,10 @@ impl<R: Reason> Inherited<R> {
220220
fn add_type_consts(
221221
&mut self,
222222
opts: &GlobalOptions,
223+
child: &ShallowClass<R>,
223224
other_type_consts: TypeConstNameIndexMap<TypeConst<R>>,
224225
) {
225-
let strict_const_semantics = opts.tco_enable_strict_const_semantics > 2;
226+
let fix_synthesized = opts.tco_enable_strict_const_semantics > 3;
226227

227228
for (name, mut new_const) in other_type_consts {
228229
match self.type_consts.entry(name) {
@@ -239,15 +240,20 @@ impl<R: Reason> Inherited<R> {
239240
e.get_mut().enforceable = new_const.enforceable.clone();
240241
}
241242
let old_const = e.get();
243+
let is_class = || match child.kind {
244+
ClassishKind::Cclass(_) => true,
245+
ClassishKind::Ctrait
246+
| ClassishKind::Cinterface
247+
| ClassishKind::Cenum
248+
| ClassishKind::CenumClass(_) => false,
249+
};
242250
match (
243251
old_const.is_synthesized,
244252
new_const.is_synthesized,
245253
&old_const.kind,
246254
&new_const.kind,
247255
) {
248-
// TODO(T125402906) Re-enable this after fixing inheritance
249-
// (false, true, _, _) => {}
250-
(false, true, _, _) if strict_const_semantics => {}
256+
(false, true, _, _) if is_class() && fix_synthesized => {}
251257
// This covers the following case
252258
// ```
253259
// interface I1 { abstract const type T; }
@@ -308,7 +314,7 @@ impl<R: Reason> Inherited<R> {
308314
}
309315
}
310316

311-
fn add_inherited(&mut self, opts: &GlobalOptions, other: Self) {
317+
fn add_inherited(&mut self, opts: &GlobalOptions, child: &ShallowClass<R>, other: Self) {
312318
let Self {
313319
substs,
314320
props,
@@ -326,7 +332,7 @@ impl<R: Reason> Inherited<R> {
326332
self.add_static_methods(static_methods);
327333
self.add_constructor(constructor);
328334
self.add_consts(consts);
329-
self.add_type_consts(opts, type_consts);
335+
self.add_type_consts(opts, child, type_consts);
330336
}
331337

332338
fn mark_as_synthesized(&mut self) {
@@ -506,7 +512,7 @@ impl<'a, R: Reason> MemberFolder<'a, R> {
506512
fn add_from_interface_constants(&mut self) -> Result<()> {
507513
for ty in self.child.req_implements.iter() {
508514
self.members
509-
.add_inherited(self.opts, self.class_constants_from_class(ty)?)
515+
.add_inherited(self.opts, self.child, self.class_constants_from_class(ty)?)
510516
}
511517

512518
Ok(())
@@ -515,7 +521,7 @@ impl<'a, R: Reason> MemberFolder<'a, R> {
515521
fn add_from_implements_constants(&mut self) -> Result<()> {
516522
for ty in self.child.implements.iter() {
517523
self.members
518-
.add_inherited(self.opts, self.class_constants_from_class(ty)?)
524+
.add_inherited(self.opts, self.child, self.class_constants_from_class(ty)?)
519525
}
520526

521527
Ok(())
@@ -524,7 +530,7 @@ impl<'a, R: Reason> MemberFolder<'a, R> {
524530
fn add_from_xhp_attr_uses(&mut self) -> Result<()> {
525531
for ty in self.child.xhp_attr_uses.iter() {
526532
self.members
527-
.add_inherited(self.opts, self.xhp_attrs_from_class(ty)?)
533+
.add_inherited(self.opts, self.child, self.xhp_attrs_from_class(ty)?)
528534
}
529535

530536
Ok(())
@@ -554,7 +560,7 @@ impl<'a, R: Reason> MemberFolder<'a, R> {
554560
// be implemented.
555561
for ty in tys.iter().rev() {
556562
self.members
557-
.add_inherited(self.opts, self.members_from_class(ty)?);
563+
.add_inherited(self.opts, self.child, self.members_from_class(ty)?);
558564
}
559565

560566
Ok(())
@@ -564,7 +570,7 @@ impl<'a, R: Reason> MemberFolder<'a, R> {
564570
for ty in self.child.req_extends.iter() {
565571
let mut inherited = self.members_from_class(ty)?;
566572
inherited.mark_as_synthesized();
567-
self.members.add_inherited(self.opts, inherited);
573+
self.members.add_inherited(self.opts, self.child, inherited);
568574
}
569575

570576
Ok(())
@@ -573,7 +579,7 @@ impl<'a, R: Reason> MemberFolder<'a, R> {
573579
fn add_from_traits(&mut self) -> Result<()> {
574580
for ty in self.child.uses.iter() {
575581
self.members
576-
.add_inherited(self.opts, self.members_from_class(ty)?);
582+
.add_inherited(self.opts, self.child, self.members_from_class(ty)?);
577583
}
578584

579585
Ok(())
@@ -582,8 +588,11 @@ impl<'a, R: Reason> MemberFolder<'a, R> {
582588
fn add_from_included_enums_constants(&mut self) -> Result<()> {
583589
if let Some(et) = self.child.enum_type.as_ref() {
584590
for ty in et.includes.iter() {
585-
self.members
586-
.add_inherited(self.opts, self.class_constants_from_class(ty)?);
591+
self.members.add_inherited(
592+
self.opts,
593+
self.child,
594+
self.class_constants_from_class(ty)?,
595+
);
587596
}
588597
}
589598

hphp/hack/src/hackrs/hackrs_test_utils/decl_provider.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub fn make_folded_decl_provider<R: Reason>(
2727
shallow_decl_store: ShallowDeclStore<R>,
2828
decl_parser: DeclParser<R>,
2929
) -> impl FoldedDeclProvider<R> {
30+
let opts = Arc::new(decl_parser.opts.clone());
3031
let shallow_decl_provider: Arc<dyn ShallowDeclProvider<R>> =
3132
if let Some(naming_table_path) = naming_table {
3233
Arc::new(LazyShallowDeclProvider::new(
@@ -39,7 +40,7 @@ pub fn make_folded_decl_provider<R: Reason>(
3940
};
4041

4142
LazyFoldedDeclProvider::new(
42-
Arc::new(oxidized::global_options::GlobalOptions::default()),
43+
opts,
4344
match store_opts {
4445
StoreOpts::Serialized(compression_type) => {
4546
Arc::new(SerializingStore::with_compression(compression_type))

hphp/hack/src/providers/hackrs_provider_backend/hackrs_provider_backend.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ impl HhServerProviderBackend {
9595
))));
9696

9797
let folded_decl_provider = Arc::new(LazyFoldedDeclProvider::new(
98-
Arc::new(Default::default()), // TODO: remove?
98+
Arc::new(parser_options.clone()),
9999
Arc::clone(&folded_classes_store) as _,
100100
Arc::clone(&lazy_shallow_decl_provider) as _,
101101
dependency_graph,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
--enable-strict-const-semantics 2
1+
--enable-strict-const-semantics 4
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?hh
2+
// (c) Meta Platforms, Inc. and affiliates.
3+
4+
interface ISource {
5+
const type T = int;
6+
}
7+
8+
trait TSource {
9+
require implements ISource;
10+
}
11+
12+
abstract class Grandparent implements ISource {
13+
use TSource;
14+
}
15+
16+
trait TGrandparent {
17+
require extends Grandparent;
18+
}
19+
20+
abstract class Parentt extends Grandparent {
21+
use TGrandparent;
22+
}
23+
24+
trait TParent {
25+
require extends Parentt;
26+
const type T = int;
27+
}
28+
29+
final class Child extends Parentt {
30+
use TParent;
31+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
File "synthesized_const_bug.php", line 29, characters 13-17:
2+
Constant T is defined concretely in multiple ancestors (Typing[4374])
3+
File "synthesized_const_bug.php", line 5, characters 14-14:
4+
One conflicting definition is here, inherited through Parentt
5+
File "synthesized_const_bug.php", line 26, characters 14-14:
6+
Another conflicting definition is here

0 commit comments

Comments
 (0)