Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 9ee2a50

Browse files
committed
fix(group_theory/group_action): has_scalar.comp.is_scalar_tower is a dangerous instance (#9656)
This issue came up in the discussion of #9535: in certain cases, the instance `has_scalar.comp.is_scalar_tower` is fed too many metavariables and starts recursing infinitely. So I believe we should demote it from being an instance. Example trace: ```plain [class_instances] (0) ?x_0 : has_scalar S P.quotient := @quotient.has_scalar ?x_1 ?x_2 ?x_3 ?x_4 ?x_5 ?x_6 ?x_7 ?x_8 ?x_9 ?x_10 [class_instances] (1) ?x_9 : @is_scalar_tower S R M ?x_7 (@smul_with_zero.to_has_scalar R M (@mul_zero_class.to_has_zero R (@mul_zero_one_class.to_mul_zero_class R (@monoid_with_zero.to_mul_zero_one_class R (@semiring.to_monoid_with_zero R (@ring.to_semiring R _inst_1))))) (@add_zero_class.to_has_zero M (@add_monoid.to_add_zero_class M (@add_comm_monoid.to_add_monoid M (@add_comm_group.to_add_comm_monoid M _inst_2)))) (@mul_action_with_zero.to_smul_with_zero R M (@semiring.to_monoid_with_zero R (@ring.to_semiring R _inst_1)) (@add_zero_class.to_has_zero M (@add_monoid.to_add_zero_class M (@add_comm_monoid.to_add_monoid M (@add_comm_group.to_add_comm_monoid M _inst_2)))) (@module.to_mul_action_with_zero R M (@ring.to_semiring R _inst_1) (@add_comm_group.to_add_comm_monoid M _inst_2) _inst_3))) ?x_8 := @has_scalar.comp.is_scalar_tower ?x_11 ?x_12 ?x_13 ?x_14 ?x_15 ?x_16 ?x_17 ?x_18 ?x_19 [class_instances] (2) ?x_18 : @is_scalar_tower ?x_11 R M ?x_15 (@smul_with_zero.to_has_scalar R M (@mul_zero_class.to_has_zero R (@mul_zero_one_class.to_mul_zero_class R (@monoid_with_zero.to_mul_zero_one_class R (@semiring.to_monoid_with_zero R (@ring.to_semiring R _inst_1))))) (@add_zero_class.to_has_zero M (@add_monoid.to_add_zero_class M (@add_comm_monoid.to_add_monoid M (@add_comm_group.to_add_comm_monoid M _inst_2)))) (@mul_action_with_zero.to_smul_with_zero R M (@semiring.to_monoid_with_zero R (@ring.to_semiring R _inst_1)) (@add_zero_class.to_has_zero M (@add_monoid.to_add_zero_class M (@add_comm_monoid.to_add_monoid M (@add_comm_group.to_add_comm_monoid M _inst_2)))) (@module.to_mul_action_with_zero R M (@ring.to_semiring R _inst_1) (@add_comm_group.to_add_comm_monoid M _inst_2) _inst_3))) ?x_16 := @has_scalar.comp.is_scalar_tower ?x_20 ?x_21 ?x_22 ?x_23 ?x_24 ?x_25 ?x_26 ?x_27 ?x_28 ... ``` You'll see that the places where `has_scalar.comp.is_scalar_tower` expects a `has_scalar.comp` are in fact metavariables, so they always unify. I have included a test case where the instance looks innocuous enough in its parameters: everything is phrased in terms of either irreducible defs, implicit variables or instance implicits, to argue that the issue really lies with `has_scalar.comp.is_scalar_tower`. Moreover, I don't think we lose a lot by demoting the latter from an instance since `has_scalar.comp` isn't an instance either. Co-authored-by: Anne Baanen <Vierkantor@users.noreply.github.com>
1 parent e8427b0 commit 9ee2a50

3 files changed

Lines changed: 49 additions & 2 deletions

File tree

src/group_theory/group_action/defs.lean

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,15 @@ def comp (g : N → M) : has_scalar N α :=
173173

174174
variables {α}
175175

176-
/-- If an action forms a scalar tower then so does the action formed by `has_scalar.comp`. -/
176+
/-- Given a tower of scalar actions `M → α → β`, if we use `has_scalar.comp`
177+
to pull back both of `M`'s actions by a map `g : N → M`, then we obtain a new
178+
tower of scalar actions `N → α → β`.
179+
180+
This cannot be an instance because it can cause infinite loops whenever the `has_scalar` arguments
181+
are still metavariables.
182+
-/
177183
@[priority 100]
178-
instance comp.is_scalar_tower [has_scalar M β] [has_scalar α β] [is_scalar_tower M α β]
184+
lemma comp.is_scalar_tower [has_scalar M β] [has_scalar α β] [is_scalar_tower M α β]
179185
(g : N → M) :
180186
(by haveI := comp α g; haveI := comp β g; exact is_scalar_tower N α β) :=
181187
by exact {smul_assoc := λ n, @smul_assoc _ _ _ _ _ _ _ (g n) }

src/linear_algebra/basis.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ variables {R' : Type*} [semiring R'] [module R' M] (f : R ≃+* R') (h : ∀ c (
281281

282282
include f h b
283283

284+
local attribute [instance] has_scalar.comp.is_scalar_tower
285+
284286
/-- If `R` and `R'` are isomorphic rings that act identically on a module `M`,
285287
then a basis for `M` as `R`-module is also a basis for `M` as `R'`-module.
286288

test/has_scalar_comp_loop.lean

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import group_theory.group_action.basic
2+
3+
variables (R M S : Type*)
4+
5+
/-- Some arbitrary type depending on `has_scalar R M` -/
6+
@[irreducible, nolint has_inhabited_instance unused_arguments]
7+
def foo [has_scalar R M] : Type* := ℕ
8+
9+
variables [has_scalar R M] [has_scalar S R] [has_scalar S M]
10+
11+
/-- This instance is incompatible with `has_scalar.comp.is_scalar_tower`.
12+
However, all its parameters are (instance) implicits or irreducible defs, so it
13+
should not be dangerous. -/
14+
@[nolint unused_arguments]
15+
instance foo.has_scalar [is_scalar_tower S R M] : has_scalar S (foo R M) :=
16+
⟨λ _ _, by { unfold foo, exact 37 }⟩
17+
18+
-- If there is no `is_scalar_tower S R M` parameter, this should fail quickly,
19+
-- not loop forever.
20+
example : has_scalar S (foo R M) :=
21+
begin
22+
tactic.success_if_fail_with_msg tactic.interactive.apply_instance
23+
"tactic.mk_instance failed to generate instance for
24+
has_scalar S (foo R M)",
25+
unfold foo,
26+
exact ⟨λ _ _, 37
27+
end
28+
29+
/-
30+
local attribute [instance] has_scalar.comp.is_scalar_tower
31+
-- When `has_scalar.comp.is_scalar_tower` is an instance, this recurses indefinitely.
32+
example : has_scalar S (foo R M) :=
33+
begin
34+
tactic.success_if_fail_with_msg tactic.interactive.apply_instance
35+
"maximum class-instance resolution depth has been reached (the limit can be increased by setting option 'class.instance_max_depth') (the class-instance resolution trace can be visualized by setting option 'trace.class_instances')",
36+
unfold foo,
37+
exact ⟨λ _ _, 37⟩
38+
end
39+
-/

0 commit comments

Comments
 (0)