Skip to content

Commit 767fb23

Browse files
committed
typing: resolve record projections from the record type
Field projections (e.`f) were resolved by looking up the field name as an operator, which requires the projection operator -- and hence the theory declaring the record type -- to be in scope. As a result, a projection through a type from a non-imported theory failed. When the record type is known, resolve the projector directly from the type: the projection operator lives in the theory declaring the type and is reachable by its qualified name even when that theory is not imported. This also disambiguates same-named fields of unrelated records by type. Fall back to the name-based search only when the type is not yet known.
1 parent ffd6b90 commit 767fb23

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

src/ecTyping.ml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,8 +447,30 @@ let select_form_op env mode ~forcepv opsc name ue tvi psig =
447447
(* -------------------------------------------------------------------- *)
448448
let select_proj env opsc name ue tvi recty =
449449
let filter = (fun _ op -> EcDecl.is_proj op) in
450-
let ops = EcUnify.select_op ~filter tvi env name ue ([recty], None) in
451-
let ops = List.map (fun (p, ty, ue, _) -> (p, ty, ue)) ops in
450+
let do_select name =
451+
let ops = EcUnify.select_op ~filter tvi env name ue ([recty], None) in
452+
List.map (fun (p, ty, ue, _) -> (p, ty, ue)) ops in
453+
454+
(* When the record type is known, resolve the projector from the type so it
455+
need not be in scope by name; fall back to a name-based search otherwise. *)
456+
let ty = ty_subst (Tuni.subst (UE.assubst ue)) recty in
457+
let ops =
458+
match (EcEnv.ty_hnorm ty env).ty_node with
459+
| Tconstr (tp, _) -> begin
460+
let projp = EcPath.pqoname (EcPath.prefix tp) (snd name) in
461+
match EcEnv.Op.by_path_opt projp env with
462+
| Some op when EcDecl.is_proj op
463+
&& EcPath.p_equal tp (proj3_1 (EcDecl.operator_as_proj op)) ->
464+
let subue = EcUnify.UniEnv.copy ue in
465+
let top, tvs =
466+
EcUnify.UniEnv.openty subue op.op_tparams tvi op.op_ty in
467+
(try EcUnify.unify env subue top (EcUnify.tfun_expected subue [recty])
468+
with EcUnify.UnificationFailure _ -> assert false);
469+
[((projp, tvs), top, subue)]
470+
| _ -> do_select name
471+
end
472+
| _ -> do_select name
473+
in
452474

453475
match ops, opsc with
454476
| _ :: _ :: _, Some opsc ->

tests/record-proj-unimported.ec

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
(* Record field projections must resolve from the record type alone, even when
2+
the theory declaring the type (and hence the projection operator) is not
3+
imported in the current scope. See issue #1011. *)
4+
5+
theory T.
6+
type t = { f : unit }.
7+
op e : t.
8+
end T.
9+
10+
(* `T` is not imported: `f` is not in scope by its bare name, but the
11+
projection is determined by the type of `T.e`. *)
12+
op test = T.e.`f.
13+
14+
(* Same field name in two unrelated records: the known type disambiguates. *)
15+
theory A. type t = { g : int }. op e : t. end A.
16+
theory B. type t = { g : bool }. op e : t. end B.
17+
18+
op ta : int = A.e.`g.
19+
op tb : bool = B.e.`g.
20+
21+
(* Projection through a clone that inlines the record type. *)
22+
theory U.
23+
type u.
24+
op e : u.
25+
end U.
26+
27+
type u' = { h : int }.
28+
29+
clone U as U' with type u <- u'.
30+
31+
op tc : int = U'.e.`h.

0 commit comments

Comments
 (0)