Skip to content

Commit 24314e3

Browse files
authored
Merge branch 'master' into joachim/less-isNoConfusion
2 parents 0ca0ab4 + 5bf5c73 commit 24314e3

File tree

922 files changed

+287662
-291651
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

922 files changed

+287662
-291651
lines changed

.github/workflows/grove.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
- name: Fetch upstream invalidated facts
5252
if: ${{ steps.should-run.outputs.should-run == 'true' && steps.workflow-info.outputs.pullRequestNumber != '' }}
5353
id: fetch-upstream
54-
uses: TwoFx/grove-action/fetch-upstream@v0.4
54+
uses: TwoFx/grove-action/fetch-upstream@v0.5
5555
with:
5656
artifact-name: grove-invalidated-facts
5757
base-ref: master
@@ -96,7 +96,7 @@ jobs:
9696
- name: Build
9797
if: ${{ steps.should-run.outputs.should-run == 'true' }}
9898
id: build
99-
uses: TwoFx/grove-action/build@v0.4
99+
uses: TwoFx/grove-action/build@v0.5
100100
with:
101101
project-path: doc/std/grove
102102
script-name: grove-stdlib

doc/std/grove/GroveStdlib/Std/CoreTypesAndOperations/StringsAndFormatting.lean

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,41 @@ namespace GroveStdlib.Std.CoreTypesAndOperations
1111

1212
namespace StringsAndFormatting
1313

14+
open Lean Meta
15+
16+
def introduction : Text where
17+
id := "string-introduction"
18+
content := Grove.Markdown.render [
19+
.h1 "The Lean string library",
20+
.text "The Lean standard library contains a fully-featured string library, centered around the types `String` and `String.Slice`.",
21+
.text "`String` is defined as the subtype of `ByteArray` of valid UTF-8 strings. A `String.Slice` is a `String` together with a start and end position.",
22+
.text "`String` is equivalent to `List Char`, but it has a more efficient runtime representation. While the logical model based on `ByteArray` is overwritten in the runtime, the runtime implementation is very similar to the logical model, with the main difference being that the length of a string in Unicode code points is cached in the runtime implementation.",
23+
.text "We are considering removing this feature in the future (i.e., deprecating `String.length`), as the number of UTF-8 codepoints in a string is not particularly useful, and if needed it can be computed in linear time using `s.positions.count`."
24+
]
25+
1426
def highLevelStringTypes : List Lean.Name :=
1527
[`String, `String.Slice, `String.Pos, `String.Slice.Pos]
1628

29+
def creatingStringsAndSlices : Text where
30+
id := "transforming-strings-and-slices"
31+
content := Grove.Markdown.render [
32+
.h2 "Transforming strings and slices",
33+
.text "The Lean standard library contains a number of functions that take one or more strings and slices and return a string or a slice.",
34+
.text "If possible, these functions should avoid allocating a new string, and return a slice of their input(s) instead.",
35+
.text "Usually, for every operation `f`, there will be functions `String.f` and `String.Slice.f`, where `String.f s` is defined as `String.Slice.f s.toSlice`.",
36+
.text "In particular, functions that transform strings and slices should live in the `String` and `String.Slice` namespaces even if they involve a `String.Pos`/`String.Slice.Pos` (like `String.sliceTo`), for reasons that will become clear shortly.",
37+
38+
.h3 "Transforming positions",
39+
.text "Since positions on strings and slices are dependent on the string or slice, whenever users transform a string/slice, they will be interested in interpreting positions on the original string/slice as positions on the result, or vice versa.",
40+
.text "Consequently, every operation that transforms a string or slice should come with a corresponding set of transformations between positions, usually in both directions, possibly with one of the directions being conditional.",
41+
.text "For example, given a string `s` and a position `p` on `s`, we have the slice `s.sliceFrom p`, which is the slice from `p` to the end of `s`. A position on `s.sliceFrom p` can always be interpreted as a position on `s`. This is the \"backwards\" transformation. Conversely, a position `q` on `s` can be interpreted as a position on `s.sliceFrom p` as long as `p ≤ q`. This is the conditional forwards direction.",
42+
.text "The convention for naming these transformations is that the forwards transformation should have the same name as the transformation on strings/slices, but it should be located in the `String.Pos` or `String.Slice.Pos` namespace, depending on the type of the starting position (so that dot notation is possible for the forward direction). The backwards transformation should have the same name as the operation on strings/slices, but with an `of` prefix, and live in the same namespace as the forwards transformation (so in general dot notation will not be available).",
43+
.text "So, in the `sliceFrom` example, the forward direction would be called `String.Pos.sliceFrom`, while the backwards direction should be called `String.Pos.ofSliceFrom` (not `String.Slice.Pos.ofSliceFrom`).",
44+
.text "If one of the directions is conditional, it should have a corresponding panicking operation that does not require a proof; in our example this would be `String.Pos.sliceFrom!`.",
45+
.text "Sometimes there is a name clash for the panicking operations if the operation on strings is already panicking. For example, there are both `String.slice` and `String.slice!`. If the original operation is already panicking, we only provide panicking transformation operations. But now `String.Pos.slice!` could refer both to the panicking forwards transformation associated with `String.slice`, and also to the (only) forwards transformation associated with `String.slice!`. In this situation, we use an `orPanic` suffix to disambiguate. So the panicking forwards operation associated with `String.slice` is called `String.Pos.sliceOrPanic`, and the forwards operation associated with `String.slice!` is called `String.Pos.slice!`."
46+
]
47+
48+
-- TODO: also include the `HAppend` instance(s)
1749
def sliceProducing : AssociationTable (β := Alias Lean.Name) .declaration
1850
[`String, `String.Slice,
1951
Alias.mk `String.Pos "string-pos-forwards" "String.Pos (forwards)",
@@ -23,16 +55,43 @@ def sliceProducing : AssociationTable (β := Alias Lean.Name) .declaration
2355
Alias.mk `String.Slice.Pos "string-slice-pos-backwards" "String.Slice.Pos (backwards)",
2456
Alias.mk `String.Slice.Pos "string-slice-pos-noproof" "String.Slice.Pos (no proof)"] where
2557
id := "slice-producing"
26-
title := "String functions returning slices"
58+
title := "String functions returning strings or slices"
2759
description := "Operations on strings and string slices that themselves return a new string slice."
2860
dataSources n := DataSource.definitionsInNamespace n.inner
2961

62+
def sliceProducingComplete : Assertion where
63+
widgetId := "slice-producing-complete"
64+
title := "Slice-producing table is complete"
65+
description := "All functions in the `String.**` namespace that return a string or a slice are covered in the table"
66+
check := do
67+
let mut ans := #[]
68+
let covered := Std.HashSet.ofArray (← valuesInAssociationTable sliceProducing)
69+
let pred : DataSource.DeclarationPredicate :=
70+
DataSource.DeclarationPredicate.all [.isDefinition, .not .isDeprecated,
71+
.notInNamespace `String.Pos.Raw, .notInNamespace `String.Legacy,
72+
.not .isInstance]
73+
let env ← getEnv
74+
for name in ← declarationsMatching `String pred do
75+
let some c := env.find? name | continue
76+
if c.type.getForallBody.getUsedConstants.any (fun n => n == ``String || n == ``String.Slice) then
77+
let success : Bool := name.toString ∈ covered
78+
ans := ans.push {
79+
assertionId := name.toString
80+
description := s!"`{name}` should appear in the table."
81+
passed := success
82+
message := s!"`{name}` was{if success then "" else " not"} found in the table."
83+
}
84+
return ans
85+
3086
end StringsAndFormatting
3187

3288
open StringsAndFormatting
3389

3490
def stringsAndFormatting : Node :=
3591
.section "strings-and-formatting" "Strings and formatting"
36-
#[.associationTable sliceProducing]
92+
#[.text introduction,
93+
.text creatingStringsAndSlices,
94+
.associationTable sliceProducing,
95+
.assertion sliceProducingComplete]
3796

3897
end GroveStdlib.Std.CoreTypesAndOperations

doc/std/grove/lake-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "git",
66
"subDir": "backend",
77
"scope": "",
8-
"rev": "c19abde9101b29eb1e30d0d9d9e24ec2714f25b6",
8+
"rev": "c580a425c9b7fa2aebaec2a1d8de16b2e2283c40",
99
"name": "grove",
1010
"manifestFile": "lake-manifest.json",
1111
"inputRev": "master",

src/Lean/Elab/App.lean

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,16 @@ where
13141314
| none => 0
13151315
| some (_, p2) => prodArity p2 + 1
13161316

1317+
/--
1318+
For a fieldname `fieldName`, locate all the environment constants with that name
1319+
-/
1320+
private def reverseFieldLookup (env : Environment) (fieldName : String) :=
1321+
env.constants.fold (init := #[]) (fun accum name _ =>
1322+
match name with
1323+
| .str _ s => if s = fieldName && !name.isInternal then accum.push name else accum
1324+
| _ => accum)
1325+
|>.qsort (lt := Name.lt)
1326+
13171327
private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM LValResolution := do
13181328
match eType.getAppFn, lval with
13191329
| .const structName _, LVal.fieldIdx ref idx =>
@@ -1379,11 +1389,7 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
13791389
has function type{inlineExprTrailing eType}"
13801390

13811391
| .mvar .., .fieldName _ fieldName _ _ =>
1382-
let possibleConstants := (← getEnv).constants.fold (fun accum name _ =>
1383-
match name with
1384-
| .str _ s => if s = fieldName && !name.isInternal then accum.push name else accum
1385-
| _ => accum) #[]
1386-
let hint := match possibleConstants with
1392+
let hint := match reverseFieldLookup (← getEnv) fieldName with
13871393
| #[] => MessageData.nil
13881394
| #[opt] => .hint' m!"Consider replacing the field projection `.{fieldName}` with a call to the function `{.ofConstName opt}`."
13891395
| opts => .hint' m!"Consider replacing the field projection with a call to one of the following:\
@@ -1428,16 +1434,15 @@ where
14281434
(suggestions.map fun suggestion => {
14291435
preInfo? := .some s!"{e}.",
14301436
suggestion := suggestion.getString!,
1431-
toCodeActionTitle? := .some (s!"Suggested replacement: {e}.{·}"),
1437+
toCodeActionTitle? := .some (s!"Change to {e}.{·}"),
14321438
diffGranularity := .all,
14331439
})
14341440
else
14351441
MessageData.hint (ref? := ref)
14361442
m!"Perhaps you meant one of these in place of `{fullName}`:"
14371443
(suggestions.map fun suggestion => {
14381444
suggestion := suggestion.getString!,
1439-
toCodeActionTitle? := .some (s!"Suggested replacement: {e}.{·}"),
1440-
diffGranularity := .all,
1445+
toCodeActionTitle? := .some (s!"Change to {e}.{·}"),
14411446
messageData? := .some m!"`{.ofConstName suggestion}`: {e}.{suggestion.getString!}",
14421447
})
14431448

@@ -1765,8 +1770,17 @@ private partial def resolveDottedIdentFn (idRef : Syntax) (id : Name) (expectedT
17651770
withForallBody expectedType fun resultType => do
17661771
go resultType expectedType #[]
17671772
where
1768-
throwNoExpectedType :=
1769-
throwNamedError lean.invalidDottedIdent "Invalid dotted identifier notation: The expected type of `.{id}` could not be determined"
1773+
throwNoExpectedType := do
1774+
let hint ← match reverseFieldLookup (← getEnv) (id.getString!) with
1775+
| #[] => pure MessageData.nil
1776+
| suggestions =>
1777+
let oneOfThese := if h : suggestions.size = 1 then m!"`{.ofConstName suggestions[0]}" else m!"one of these"
1778+
m!"Using {oneOfThese} would be unambiguous:".hint (suggestions.map fun suggestion => {
1779+
suggestion := mkIdent suggestion
1780+
toCodeActionTitle? := .some (s!"Change to {·}")
1781+
messageData? := .some m!"`{.ofConstName suggestion}`",
1782+
})
1783+
throwNamedError lean.invalidDottedIdent (m!"Invalid dotted identifier notation: The expected type of `.{id}` could not be determined" ++ hint)
17701784
/-- A weak version of forallTelescopeReducing that only uses whnfCore, to avoid unfolding definitions except by `unfoldDefinition?` below. -/
17711785
withForallBody {α} (type : Expr) (k : Expr → TermElabM α) : TermElabM α := do
17721786
let type ← whnfCoreUnfoldingAnnotations type

src/Lean/Elab/PatternVar.lean

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -67,35 +67,13 @@ private def throwCtorExpected {α} (ident : Option Syntax) : M α := do
6767

6868
if candidates.size = 0 then
6969
throwError message
70-
else if h : candidates.size = 1 then
71-
throwError message ++ .hint' m!"`{candidates[0]}` is similar"
72-
else
73-
let sorted := candidates.qsort (·.toString < ·.toString)
74-
let diff :=
75-
if candidates.size > 10 then [m!" (or {candidates.size - 10} others)"]
76-
else []
77-
let suggestions : MessageData := .group <|
78-
.joinSep ((sorted.extract 0 10 |>.toList |>.map (showName env)) ++ diff)
79-
("," ++ Format.line)
80-
throwError message ++ .group (.hint' ("These are similar:" ++ .nestD (Format.line ++ suggestions)))
81-
where
82-
-- Create some `MessageData` for a name that shows it without an `@`, but with the metadata that
83-
-- makes infoview hovers and the like work. This technique only works because the names are known
84-
-- to be global constants, so we don't need the local context.
85-
showName (env : Environment) (n : Name) : MessageData :=
86-
let params :=
87-
env.constants.find?' n |>.map (·.levelParams.map Level.param) |>.getD []
88-
.ofFormatWithInfos {
89-
fmt := "'" ++ .tag 0 (format n) ++ "'",
90-
infos :=
91-
.ofList [(0, .ofTermInfo {
92-
lctx := .empty,
93-
expr := .const n params,
94-
stx := .ident .none (toString n).toRawSubstring n [.decl n []],
95-
elaborator := `Delab,
96-
expectedType? := none
97-
})] _
98-
}
70+
let oneOfThese := if h : candidates.size = 1 then m!"`{candidates[0]}`" else m!"one of these"
71+
let hint ← m!"Using {oneOfThese} would be valid:".hint (ref? := idStx) (candidates.map fun candidate => {
72+
suggestion := mkIdent candidate
73+
toCodeActionTitle? := .some (s!"Change to {·}")
74+
messageData? := .some m!"`{.ofConstName candidate}`",
75+
})
76+
throwError message ++ hint
9977

10078
private def throwInvalidPattern {α} : M α :=
10179
throwError "Invalid pattern"

src/Lean/IdentifierSuggestion.lean

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ public def throwUnknownConstantWithSuggestions (constName : Name) : CoreM α :=
9090
let modified := modifySuggestion suggestion
9191
{
9292
suggestion := s!"{modified}",
93-
toCodeActionTitle? := .some (s!"Suggested replacement: {·}"),
94-
diffGranularity := .all,
95-
-- messageData? := .some m!"replace `{.ofName rawId}` with `{.ofName modified}`",
93+
toCodeActionTitle? := .some (s!"Change to {·}"),
94+
messageData? := .some m!"`{.ofConstName suggestion}`",
9695
}) ref
9796
throwUnknownIdentifierAt (declHint := constName) ref (m!"Unknown constant `{.ofConstName constName}`" ++ hint)

src/Lean/Meta/Tactic/Try/Collect.lean

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ module
77
prelude
88
public import Init.Try
99
public import Lean.Meta.Tactic.LibrarySearch
10-
public import Lean.Meta.Tactic.Grind.Cases
11-
public import Lean.Meta.Tactic.Grind.EMatchTheorem
1210
public import Lean.Meta.Tactic.FunIndCollect
1311
import Lean.Meta.Eqns
1412
public section

src/Lean/ProjFns.lean

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,11 @@ def ProjectionFunctionInfo.fromClassEx (info : ProjectionFunctionInfo) : Bool :=
3636

3737
builtin_initialize projectionFnInfoExt : MapDeclarationExtension ProjectionFunctionInfo ← mkMapDeclarationExtension
3838

39-
@[export lean_add_projection_info]
4039
def addProjectionFnInfo (env : Environment) (projName : Name) (ctorName : Name) (numParams : Nat) (i : Nat) (fromClass : Bool) : Environment :=
4140
projectionFnInfoExt.insert env projName { ctorName, numParams, i, fromClass }
4241

4342
namespace Environment
4443

45-
@[export lean_get_projection_info]
4644
def getProjectionFnInfo? (env : Environment) (projName : Name) : Option ProjectionFunctionInfo :=
4745
projectionFnInfoExt.find? env projName
4846

0 commit comments

Comments
 (0)