Skip to content

Commit ece95ed

Browse files
committed
db-closeness: more annotation, and find some bugs
1 parent b8c36ab commit ece95ed

File tree

13 files changed

+129
-88
lines changed

13 files changed

+129
-88
lines changed

base/src/main/java/org/aya/tyck/pat/ClauseTycker.java

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
import org.aya.syntax.concrete.Expr;
1616
import org.aya.syntax.concrete.Pattern;
1717
import org.aya.syntax.core.Jdg;
18+
import org.aya.syntax.core.annotation.Closed;
1819
import org.aya.syntax.core.def.FnClauseBody;
1920
import org.aya.syntax.core.pat.Pat;
2021
import org.aya.syntax.core.pat.PatToTerm;
2122
import org.aya.syntax.core.term.*;
22-
import org.aya.syntax.ref.GenerateKind;
2323
import org.aya.syntax.ref.LocalCtx;
2424
import org.aya.syntax.ref.LocalVar;
25+
import org.aya.syntax.telescope.AbstractTele;
2526
import org.aya.tyck.ExprTycker;
2627
import org.aya.tyck.TyckState;
2728
import org.aya.tyck.ctx.LocalLet;
@@ -68,26 +69,27 @@ public record TyckResult(@NotNull ImmutableSeq<Pat.Preclause<Term>> clauses, boo
6869
}
6970
}
7071

71-
/**
72-
* @param result the result according to the pattern tycking, the
73-
* {@link DepTypeTerm.Unpi#params} is always empty if the signature result is
74-
* {@link org.aya.tyck.pat.iter.Pusheenable.Const}
75-
* @param paramSubst substitution for parameter, in the same ordeer as parameter.
76-
* See {@link PatternTycker#paramSubst}. Only used by ExprTycker, see {@link #dumpLocalLetTo}
77-
* @param freePats a free version of the patterns, see {@link #freePats()}
78-
* @param asSubst substitution of the {@code as} patterns
79-
* @implNote If there are fewer pats than parameters, there will be some pats inserted,
80-
* but this will not affect {@code paramSubst}, and the inserted pat are "ignored" in tycking
81-
* of the body, because we check the body against to {@link #result}.
82-
* Then we apply the inserted pats to the body to complete it.
83-
*/
72+
/// @param result the result according to the pattern tycking, the
73+
/// [#params] is always empty if the signature result is
74+
/// [org.aya.tyck.pat.iter.Pusheenable.Const].
75+
/// The result is always [Closed] under [#localCtx]
76+
/// @param paramSubst substitution for parameter, in the same ordeer as parameter.
77+
/// [Closed] under [#localCtx]
78+
/// Only used by ExprTycker, see [#dumpLocalLetTo]
79+
/// @param freePats free version of the patterns, [Closed] under [#localCtx()]
80+
/// @param asSubst substitution of the `as` patterns, [Closed] under [#localCtx]
81+
/// @implNote TL;DR: `freePats.dropLast(unpiParamSize)` is compatible with [#localCtx], [#paramSubst], [#result] and [#body]
82+
/// If there are fewer pats than parameters, there will be some pats inserted,
83+
/// but this will not affect `paramSubst`, and the inserted pat are "ignored" in tycking
84+
/// of the body, because we check the body against to [#result].
85+
/// Then we apply the inserted pats to the body to complete it.
8486
public record LhsResult(
8587
@NotNull LocalCtx localCtx,
86-
@NotNull Term result, int unpiParamSize,
87-
@NotNull ImmutableSeq<Pat> freePats,
88+
@NotNull @Closed Term result, int unpiParamSize,
89+
@NotNull ImmutableSeq<@Closed Pat> freePats,
8890
@Override @NotNull SourcePos sourcePos,
8991
@Nullable WithPos<Expr> body,
90-
@NotNull ImmutableSeq<Jdg> paramSubst,
92+
@NotNull ImmutableSeq<@Closed Jdg> paramSubst,
9193
@NotNull LocalLet asSubst,
9294
boolean hasError
9395
) implements SourceNode {
@@ -248,14 +250,14 @@ public record Worker(
248250

249251
// fill missing patterns
250252
// This is not a typo of "repl"
251-
var instRepi = sigIter.unpiBody().makePi().instTele(patResult.paramSubst().view().map(Jdg::wellTyped));
252-
var instUnpiParam = DepTypeTerm.unpiUnsafe(instRepi, UnaryOperator.identity(), userUnpiSize);
253-
var missingPats = instUnpiParam.params().mapIndexed((idx, x) ->
254-
// It would be nice if we have a SourcePos here
255-
new Pat.Bind(new LocalVar("unpi" + idx, SourcePos.NONE, GenerateKind.Basic.Tyck),
256-
x.type()));
257-
258-
var wellTypedPats = patResult.wellTyped().appendedAll(missingPats);
253+
@Closed var instRepi = sigIter.unpiBody().makePi()
254+
// safe to inst, as paramSubst is closed
255+
.instTele(patResult.paramSubst().view().map(Jdg::wellTyped));
256+
var instUnpiParam = DepTypeTerm.unpiUnsafe(instRepi, userUnpiSize);
257+
var freeParam = AbstractTele.enrich(new AbstractTele.Locns(instUnpiParam.params(), instUnpiParam.body()));
258+
var missingPats = freeParam.map((x) -> new Pat.Bind(x.ref(), x.type()));
259+
260+
ImmutableSeq<@Closed Pat> wellTypedPats = patResult.wellTyped().appendedAll(missingPats);
259261
return new LhsResult(ctx, instRepi, userUnpiSize,
260262
wellTypedPats, clause.sourcePos, patIter.exprBody(),
261263
patResult.paramSubst(), patResult.asSubst(), patResult.hasError());
@@ -292,16 +294,18 @@ else if (result.hasError) {
292294
exprTycker.solveMetas();
293295
var zonkBody = zonker.zonk(rawCheckedBody);
294296

297+
// eta body with inserted patterns
298+
// make before [Pat.collectVariables], as we need [pats] are [Closed].
299+
@Closed var insertPatternBody = AppTerm.make(zonkBody, pats.view().takeLast(result.unpiParamSize).map(PatToTerm::visit));
300+
var insertLetBody = makeLet(exprTycker.localLet(), insertPatternBody);
301+
295302
// bind all pat bindings
296-
var patWithTypeBound = Pat.collectVariables(result.freePats().view());
303+
var patWithTypeBound = Pat.collectVariables(pats.view());
304+
// now pats is Bound
297305
pats = patWithTypeBound.component2();
298306
var patBindTele = patWithTypeBound.component1();
299307

300308
bindCount = patBindTele.size();
301-
302-
// eta body with inserted patterns
303-
var insertPatternBody = AppTerm.make(zonkBody, pats.view().takeLast(result.unpiParamSize).map(PatToTerm::visit));
304-
var insertLetBody = makeLet(exprTycker.localLet(), insertPatternBody);
305309
wellBody = insertLetBody.bindTele(patBindTele.view());
306310
}
307311

@@ -371,7 +375,7 @@ public static void apply(@NotNull Pattern pat) {
371375
/// contain let bindings from real let expressions,
372376
///
373377
/// @param term a free term
374-
public static @NotNull Term makeLet(@NotNull LocalLet lets, @NotNull Term term) {
378+
public static @NotNull Term makeLet(@NotNull LocalLet lets, @NotNull @Closed Term term) {
375379
// only one level
376380
return lets.let()
377381
.toSeq()

base/src/main/java/org/aya/tyck/pat/PatternTycker.java

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import org.aya.syntax.concrete.Pattern;
1818
import org.aya.syntax.concrete.stmt.decl.DataDecl;
1919
import org.aya.syntax.core.Jdg;
20+
import org.aya.syntax.core.annotation.Bound;
21+
import org.aya.syntax.core.annotation.Closed;
2022
import org.aya.syntax.core.def.*;
2123
import org.aya.syntax.core.pat.Pat;
2224
import org.aya.syntax.core.pat.PatMatcher;
@@ -59,10 +61,11 @@ public class PatternTycker implements Problematic, Stateful {
5961
/// [org.aya.syntax.core.term.LocalTerm])
6062
private final @NotNull SignatureIterator telescope;
6163

62-
/** Substitution for parameter, in the same order as parameter */
63-
private final @NotNull MutableList<Jdg> paramSubst;
64+
/// Substitution for parameter, in the same order as parameter
65+
private final @NotNull MutableList<@Closed Jdg> paramSubst;
6466

65-
/// Substitution for `as` pattern
67+
/// Substitution for `as` pattern.
68+
/// Assertion: `asSubst.parent == null`
6669
private final @NotNull LocalLet asSubst;
6770

6871
/// Almost equivalent to {@code telescope.peek()}, but we may instantiate it.
@@ -101,8 +104,8 @@ public PatternTycker(
101104
}
102105

103106
public record TyckResult(
104-
@NotNull ImmutableSeq<Pat> wellTyped,
105-
@NotNull ImmutableSeq<Jdg> paramSubst,
107+
@NotNull ImmutableSeq<@Closed Pat> wellTyped,
108+
@NotNull ImmutableSeq<@Closed Jdg> paramSubst,
106109
@NotNull LocalLet asSubst,
107110
boolean hasError
108111
) { }
@@ -114,7 +117,7 @@ public record TyckResult(
114117
* @param type the type of {@param pattern}, it probably contains {@link MetaPatTerm}
115118
* @return a well-typed {@link Pat}, but still need to be inline!
116119
*/
117-
private @NotNull Pat doTyck(@NotNull WithPos<Pattern> pattern, @NotNull Term type) {
120+
private @NotNull @Closed Pat doTyck(@NotNull WithPos<Pattern> pattern, @NotNull @Closed Term type) {
118121
return switch (pattern.data()) {
119122
case Pattern.Absurd _ -> {
120123
var selection = makeSureEmpty(type, pattern);
@@ -215,19 +218,19 @@ private void consumeParam() {
215218
telescope.next();
216219
}
217220

218-
private record FindNextParam(@NotNull ImmutableSeq<Pat> generated, @NotNull Kind kind) {
221+
private record FindNextParam(@NotNull ImmutableSeq<@Closed Pat> generated, @NotNull Kind kind) {
219222
public enum Kind {
220223
Success, TooManyPattern, TooManyImplicit
221224
}
222225
}
223226

224-
/// Find next param until the predicate success
227+
/// Find next param until the predicate success.
225228
///
226229
/// @return (generated implicit patterns, status)
227230
/// @apiNote before call: {@link #currentParam} is the last checked parameter
228231
/// after call: {@link #currentParam} is the first unchecked parameter which {@param until} success on
229232
private @NotNull FindNextParam findNextParam(@Nullable WithPos<Pattern> pattern, @NotNull Predicate<Param> until) {
230-
var generatedPats = MutableList.<Pat>create();
233+
var generatedPats = MutableList.<@Closed Pat>create();
231234

232235
peekNextParam();
233236
// loop invariant: currentParam is the first unchecked parameter if not null
@@ -263,11 +266,12 @@ public enum Kind {
263266
@NotNull PatternIterator patterns,
264267
@Nullable WithPos<Pattern> outerPattern
265268
) {
266-
var wellTyped = MutableList.<Pat>create();
269+
var wellTyped = MutableList.<@Closed Pat>create();
267270
// last user given pattern, that is, not aya generated
268271
@Nullable Arg<WithPos<Pattern>> lastPat = null;
269272

270273
// loop invariant: [patterns] points to the last checked pattern, same for [telescope]
274+
// "points to" means the last element it consumes holds the property.
271275
outer:
272276
while (patterns.hasNext()) {
273277
var currentPat = patterns.peek();
@@ -283,7 +287,7 @@ public enum Kind {
283287
var fnp = findNextParam(currentPat.term(), p ->
284288
p.explicit() == currentPat.explicit());
285289
// || telescope.isFromPusheen() == patterns.isFromPusheen()
286-
// ^this check implies the first one
290+
// ^this check is implied by the first one
287291

288292
ImmutableSeq<Pat> generated = null;
289293

@@ -312,8 +316,10 @@ public enum Kind {
312316
break;
313317
}
314318

315-
wellTyped.append(tyckPattern(currentPat.term()));
319+
wellTyped.append(tyckPattern(currentPat.term())); // tyckPattern consumes [currentParam] by move [telescope] forward
316320
patterns.next(); // consume pattern
321+
322+
// now: [patterns] points to the last checked pattern, same for [telescope]
317323
}
318324

319325
// now: ! patterns.hasNext()
@@ -323,12 +329,18 @@ public enum Kind {
323329
// all not pusheen patterns have their parameters
324330

325331
// is there any explicit parameters?
332+
// this process also generate pattern for tailing implicit parameters
326333
var generated = findNextParam(null, p ->
327334
p.explicit() || telescope.isFromPusheen());
328335
// ^this check implies the first one
329336

330337
// what kind of parameter you found?
331338
if (generated.kind == FindNextParam.Kind.Success && !telescope.isFromPusheen()) {
339+
// [findNextParam] success means at least one parameter is peeked,
340+
// thus `telescope.hasNext()` is true before the call to `findNextParam`.
341+
// Therefore, now: ! patterns.hasNext(),
342+
// other two cases are ruled out by `telescope.hasNext()` and `! telescope.isFromPusheen()`
343+
332344
// no you can't!
333345
WithPos<Pattern> errorPattern = lastPat == null
334346
? Objects.requireNonNull(outerPattern)
@@ -348,6 +360,7 @@ public enum Kind {
348360
return done(wellTyped);
349361
}
350362

363+
/// Must be called before using [#currentParam]
351364
private @NotNull Closer instCurrentParam() {
352365
currentParam = currentParam.descent(t -> t.instTele(paramSubst.view().map(Jdg::wellTyped)));
353366
return CLOSER;
@@ -361,18 +374,19 @@ private class Closer implements AutoCloseable {
361374
/**
362375
* Checking {@param pattern} with {@link PatternTycker#currentParam}
363376
*/
364-
private @NotNull Pat tyckPattern(@NotNull WithPos<Pattern> pattern) {
377+
private @NotNull @Closed Pat tyckPattern(@NotNull WithPos<Pattern> pattern) {
365378
try (var _ = instCurrentParam()) {
366379
var result = doTyck(pattern, currentParam.type());
367380
addArgSubst(result, currentParam.type());
368381
return result;
369382
}
370383
}
371384

372-
private @NotNull Pat doGeneratePattern(@NotNull Term type, @NotNull String name, @NotNull SourcePos pos) {
385+
private @NotNull @Closed Pat doGeneratePattern(@NotNull @Closed Term type, @NotNull String name, @NotNull SourcePos pos) {
373386
var freshVar = nameGen.bindName(name);
374387
if (exprTycker.whnf(type) instanceof DataCall dataCall) {
375388
// this pattern would be a Con, it can be inferred
389+
// the db-closeness of Pat.Meta is determined by [dataCall], not its solution.
376390
return new Pat.Meta(MutableValue.create(), freshVar.name(), dataCall, pos);
377391
} else {
378392
// If the type is not a DataCall, then the only available pattern is Pat.Bind
@@ -386,7 +400,7 @@ private class Closer implements AutoCloseable {
386400
* we generate a MetaPat for each,
387401
* so that they can be inferred during {@link ClauseTycker}
388402
*/
389-
private @NotNull Pat generatePattern() {
403+
private @NotNull @Closed Pat generatePattern() {
390404
try (var _ = instCurrentParam()) {
391405
// TODO: I NEED A SOURCE POS!!
392406
var pat = doGeneratePattern(currentParam.type(), currentParam.name(), SourcePos.NONE);
@@ -396,7 +410,7 @@ private class Closer implements AutoCloseable {
396410
}
397411

398412
private @NotNull ImmutableSeq<Pat> tyckInner(
399-
@NotNull ImmutableSeq<Param> telescope,
413+
@NotNull ImmutableSeq<@Bound Param> telescope,
400414
@NotNull ImmutableSeq<Arg<WithPos<Pattern>>> patterns,
401415
@NotNull WithPos<Pattern> outerPattern
402416
) {
@@ -407,25 +421,25 @@ private class Closer implements AutoCloseable {
407421
return tyckResult.wellTyped;
408422
}
409423

410-
private void addArgSubst(@NotNull Pat pattern, @NotNull Term type) {
424+
private void addArgSubst(@NotNull @Closed Pat pattern, @NotNull @Closed Term type) {
411425
paramSubst.append(new Jdg.Default(PatToTerm.visit(pattern), type));
412426
}
413427

414-
private void addAsSubst(@NotNull LocalVar as, @NotNull Pat pattern, @NotNull Term type) {
428+
private void addAsSubst(@NotNull LocalVar as, @NotNull Pat pattern, @NotNull @Closed Term type) {
415429
asSubst.put(as, new Jdg.Default(PatToTerm.visit(pattern), type), false);
416430
}
417431

418-
private @NotNull TyckResult done(@NotNull MutableList<Pat> wellTyped) {
432+
private @NotNull TyckResult done(@NotNull MutableList<@Closed Pat> wellTyped) {
419433
return new TyckResult(wellTyped.toSeq(), paramSubst.toSeq(), asSubst, hasError);
420434
}
421435

422436
private record Selection(
423-
@NotNull DataCall data,
424-
@NotNull ImmutableSeq<Term> ownerArgs,
425-
@NotNull ConCallLike.Head conHead
437+
@NotNull @Closed DataCall data,
438+
@NotNull ImmutableSeq<@Closed Term> ownerArgs,
439+
@NotNull ConCallLike.@Closed Head conHead
426440
) { }
427441

428-
private @Nullable ConCallLike.Head makeSureEmpty(Term type, @NotNull WithPos<Pattern> pattern) {
442+
private @Nullable ConCallLike.Head makeSureEmpty(@Closed Term type, @NotNull WithPos<Pattern> pattern) {
429443
if (!(exprTycker.whnf(type) instanceof DataCall dataCall)) {
430444
foundError(new PatternProblem.SplittingOnNonData(pattern, type));
431445
return null;
@@ -450,7 +464,7 @@ private record Selection(
450464
return null;
451465
}
452466

453-
private @Nullable Selection makeSureAvail(Term type, @NotNull ConDefLike name, @NotNull WithPos<Pattern> pattern) {
467+
private @Nullable Selection makeSureAvail(@Closed Term type, @NotNull ConDefLike name, @NotNull WithPos<Pattern> pattern) {
454468
if (!(exprTycker.whnf(type) instanceof DataCall dataCall)) {
455469
foundError(new PatternProblem.SplittingOnNonData(pattern, type));
456470
return null;
@@ -462,8 +476,8 @@ private record Selection(
462476

463477
return switch (checkAvail(dataCall, name, exprTycker.state)) {
464478
case Result.Ok(var subst) -> new Selection(
465-
(DataCall) dataCall.instTeleFrom(name.selfTeleSize(), subst.view()),
466-
subst, new ConCallLike.Head(name, dataCall.ulift(), subst));
479+
dataCall, subst,
480+
new ConCallLike.Head(name, dataCall.ulift(), subst));
467481
case Result.Err(_) -> {
468482
// Here, name != null, and is not in the list of checked body
469483
foundError(new PatternProblem.UnavailableCon(pattern, dataCall));
@@ -491,7 +505,7 @@ private record Selection(
491505
* Check whether {@param con} is available under {@param type}
492506
*/
493507
public static @NotNull Result<ImmutableSeq<Term>, State> checkAvail(
494-
@NotNull DataCall type, @NotNull ConDefLike con, @NotNull TyckState state
508+
@NotNull @Closed DataCall type, @NotNull ConDefLike con, @NotNull TyckState state
495509
) {
496510
return switch (con) {
497511
case JitCon jitCon -> jitCon.isAvailable(new Normalizer(state), type.args());
@@ -507,7 +521,7 @@ private record Selection(
507521
};
508522
}
509523

510-
private @NotNull Pat randomPat(Term param) {
524+
private @NotNull Pat randomPat(@Closed Term param) {
511525
return new Pat.Bind(nameGen.bindName(param), param);
512526
}
513527

base/src/main/java/org/aya/tyck/pat/iter/PiPusheen.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
package org.aya.tyck.pat.iter;
44

55
import kala.collection.immutable.ImmutableSeq;
6+
import org.aya.syntax.core.annotation.Bound;
67
import org.aya.syntax.core.term.DepTypeTerm;
78
import org.aya.syntax.core.term.Param;
89
import org.aya.syntax.core.term.Term;
910
import org.jetbrains.annotations.NotNull;
1011

11-
public final class PiPusheen implements Pusheenable<Param, @NotNull Term> {
12-
private final @NotNull ImmutableSeq<Param> unpi;
13-
private final @NotNull Term result;
12+
public final class PiPusheen implements Pusheenable<@Bound Param, @NotNull @Bound Term> {
13+
private final @NotNull ImmutableSeq<@Bound Param> unpi;
14+
private final @NotNull @Bound Term result;
1415

1516
// the index of next param
1617
private int pusheenIdx = 0;
@@ -20,17 +21,18 @@ public PiPusheen(@NotNull DepTypeTerm.Unpi unpi) {
2021
this.result = unpi.body();
2122
}
2223

23-
@Override public @NotNull Param peek() { return unpi.get(pusheenIdx); }
24-
@Override public @NotNull Term body() {
24+
@Override public @NotNull @Bound Param peek() { return unpi.get(pusheenIdx); }
25+
@Override public @NotNull @Bound Term body() {
2526
return unpiBody().makePi();
2627
}
2728

28-
public @NotNull DepTypeTerm.Unpi unpiBody() {
29+
/// Mostly [Bound], unless [#pusheenIdx] is 0 and the [org.aya.syntax.core.term.DepTypeTerm.Unpi] we use is closed.
30+
public @NotNull @Bound DepTypeTerm.Unpi unpiBody() {
2931
return new DepTypeTerm.Unpi(unpi.sliceView(pusheenIdx, unpi.size()).toSeq(), result);
3032
}
3133

3234
@Override public boolean hasNext() { return pusheenIdx < unpi.size(); }
33-
@Override public @NotNull Param next() {
35+
@Override public @NotNull @Bound Param next() {
3436
var result = peek();
3537
pusheenIdx += 1;
3638
return result;

0 commit comments

Comments
 (0)