Skip to content

Conversation

sabiwara
Copy link
Contributor

Following on #14720, I've been thinking if we could/should improve the error message when trying to use regex module attributes in guards/match scope.
This PR explores the idea, but feel free to close if this is overthinking on my part.

Before (confusing: :re.compile mentioned but not in the code)
Screenshot 2025-09-20 at 10 15 31

After
Screenshot 2025-09-20 at 10 16 06

## Shared functions

@doc false
defmacro __assert_assert_no_match_or_guard_scope__(exp) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case some code duplication is fine. Let's just reimplement the error message directly in the Regex module, since that's what we would expect library authors to do too.

Comment on lines 1021 to 1022
__assert_assert_no_match_or_guard_scope__("an escaped Regex")
:re.import(unquote(Macro.escape(exported)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of generating additional code, we should call Regex.__import__(unquote(...)), where __import__ itself is a macro which also checks the environment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds much better indeed 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arg, this might actually not work, since unlike Kernel we need to require Regex.

       quote do
            require Regex
            Regex.__import_pattern__(unquote(Macro.escape(exported)))
          end

Which then makes it impossible to compile elixir:

==> elixir (compile)
error: you are trying to use/import/require the module Regex which is currently being defined.

(although it seems weird to warn here, since it's happening within quote 🤔)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed the broken commit just in case: c1dee89

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the issue happens because we escape the struct defined by defstruct inside Regex. An easy fix is to add this clause to the top of the cond in __escape__:

        is_nil(regex.re_pattern) ->
          nil

And then it compiles fine!

Copy link
Contributor Author

@sabiwara sabiwara Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another idea to keep the emitted AST small 💡

We could leave a special meta when we escape a struct with __escape__, and add an extra hint when we emit the scope error if the meta is there? (Hint: This happened because you tried to escape a Regex).

Another benefit is that custom structs need to do nothing to benefit from this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that metadata would work? The __escape__ function can return any node and it would be hard to know which node to annotate/which node would fail.

Another option is for us to allow this to work:

require Regex

quote do
  Regex.__import__(...)
end

or even just this:

quote do
  Regex.__import__(...)
end

Especially as I thought we already annotated within quote that the module was already required...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that metadata would work? The escape function can return any node and it would be hard to know which node to annotate/which node would fail.

That's true, I didn't think this through, sorry.

or even just this:

quote do
  Regex.__import__(...)
end

That would be ideal, but isn't there a risk of allowing implicit requires?
Wouldn't this mess with the lexical tracker?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, the way it works is that we annotate it is required and consider the require in both caller and callee. At least that’s how we handle imports and they are more complex than require.

another option is to allow requiring the current module as long as it happens inside a function? As you can assume that when the function is executed the module is indeed defined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL about the required: true meta, I think it fixed it nicely 0e671f4.

@sabiwara sabiwara merged commit c9b529a into elixir-lang:main Sep 27, 2025
13 checks passed
@sabiwara sabiwara deleted the assert-scope-escape branch September 27, 2025 13:57
@sabiwara
Copy link
Contributor Author

OK to backport?

@josevalim
Copy link
Member

@sabiwara yes, let's backport!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants