Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write down what we know about Base.@pure #39954

Closed
wants to merge 2 commits into from

Conversation

henriquebecker91
Copy link
Contributor

The discussion about what is the correct @pure usage is not new. A search on Discourse brings many over the years. Unfortunately, the knowledge of the exact criteria for (safely) using @pure is inside the mind of a few, and I believe would be good if anyone could just point to the docs (or even better, the interested person arrives at the docs and do not open a thread in discourse) instead of the current continuous overhead for these same members of the community. The last such discussion is this one, and it sparked this PR.

Note this PR does not propose a new name for @pure this can be done after by another PR, the focus is making the criteria for safely using @pure as clear as possible.

The initial commit already compile some common advice found in the discourse. However, I think it would be important to answer the questions brought up by me here. I think it is important because the only criterion the current @pure documentation established (and insisted on) is no generic functions. However, a quick search on Base shows @pure functions using some generic functions like +, max, and so on. This leaves users confused: "Is generic here being used here as the opposite of built-in, or it means something else? Are some generic functions basic enough they can be used? If I am 100% sure of the method being called is it ok? The problem is not generic functions themselves but they being extended with new methods? Any extension is a problem or just the ones that may change the selected method inside a @pure function?" As it is very hard to write an useful function without any generic functions, and Base examples already break the rule, users for which @pure have shown to have a positive effect insist in knowing if their use was, or not, safe; or even if it is not safe, what are the conditions that may trigger incorrect behaviour and what exactly such incorrect behaviour would consist of.

@giordano
Copy link
Contributor

giordano commented Mar 8, 2021

Write down what we know about @Base.error

Is there a typo in the title? Side note: I don't personally like the syntax @Module.macroname, much prefer Module.@macroname

@timholy
Copy link
Sponsor Member

timholy commented Mar 8, 2021

Our current algorithm ("ask Jameson") has scaling limits, so this is a welcome improvement!

@timholy timholy changed the title Write down what we know about @Base.error Write down what we know about Base.@pure Mar 8, 2021
@helgee
Copy link
Contributor

helgee commented Mar 8, 2021

For reference: #27432

I still maintain my opinion that it should be @unsafe_pure to be in line with @unsafe_wrap etc.

base/expr.jl Outdated
1. A pure function must always return exactly (`===`) the same result for a given input.
If the return is a mutable struct this means it must always return the *same* object.
2. A pure function cannot be extended with new methods after it is called the first time.
3. A pure function cannot recurse (i.e., call itself).
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

typejoin recurses, and that seems to be okay

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed mention to recursion until we are sure in which conditions it is allowed/disallowed.

base/expr.jl Outdated
This also means a `@pure` function cannot use any global mutable state, including
generic functions. Calls to generic functions depend on method tables which are
The criteria used by Julia to deem a function pure is stricter than the one
used by most other languages, and incorrect `@pure` annotation may introduce
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

"other languages" is a weasel word that means nothing. Our definition of pure is (currently) weaker than LLVM's, for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the unnecessary comment. Now it is just "very strict".

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

I wonder if instead we should go a slightly different direction here and note that unlike "other languages", we don't require the user to mark a contexprs to tell the Julia compiler something it already knows? So that typically it should not be needed, but that instead using @pure is often used as a means of telling the compiler something it knows is false.

This wasn't true a number of years ago, but when we encounter new cases, we tend to try to add them to the compiler instead now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good thought. I will make that change.

* Removed (at least for now) the mention about recursion. If we are sure it is only allowed in some specific circumstances we should add it back.
* Stopped comparing Julia criteria with other languages.
Copy link
Contributor

@JeffreySarnoff JeffreySarnoff left a comment

Choose a reason for hiding this comment

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

I am re-editing the inline doc for @pure. Half of the changes are to promote clarity so faithful translations are more easily accomplished. Additional edits are for accuracy while keeping these inline docs sounding as other inline docs for macros do in Base. The proposed revision will be available here -- in my next review entry.


@pure is an annotation that asserts a simple function
is stateless, branchless, and entirely self-contained.
The compiler will rely on these implicit assertions
to best allow type inference to pass through the function,
and properly connect inferencing into whatever follows.

This macro is not exported. There are few situations
where it would be reasonable to declare one of your own
functions @pure. For example, this should not be used
before you have demonstrated that there is an optimization
or a type inference important to your design which is not
occurring, and using the annotation corrects the issue.

Sometimes @pure is the most appropriate way to prevent
the compiler from applying some misconstrued transformation.
That use is rarely needed. If you encounter something
similar, please raise an issue to inform the core developers.

A @pure function must be very simple and stateless,
and stateless implies the absence of errors and
and no throwing of exceptions. Pure functions must be
"self-sufficient" with regard to states and state transitions.

Nothing from outside of the function may induce change
to its activity or influence any internal behavior --
regardless of how inconsequential the influence or how
unnoticeable the behavior.

Given the same argument values,
a pure function always returns the same result.
If the result is a mutable struct, then that very
same instance must be returned given the
same argument values.

Declaring a function @pure when it does not meet
the requirements and follow the restrictions below,
may introduce compiler errors without warning.

  • pure functions always return the same result given the same arguments

    • with the same arguments given
    • returning a mutable object means returning that identical object
  • pure functions that use another function must use built-ins only, no generics
    - generic functions use external state (they dispatch through method tables)

 julia> tuple                             #  ok to use inside an `@pure` function
 tuple (built-in function)
 
 julia> length                           # do not use inside an `@pure` function
 length (generic function)                   
  • once a pure function has been called, it cannot be extended to support another signature

  • ?open for additional pure informatives?


@rryi
Copy link

rryi commented Mar 9, 2021

Some comments from a julia application (not core) programmer: I stumbled upon @pure in a situation with unsatisfactory performance - compiler did not eliminate a constant subexpression as expected. Doc on @pure warned me it-s dangerous, but did not guide me in correct use, and did not help bridging the gap between my "common understanding" of a pure function and the semantics and pragmatics of @pure in julia.

The current proposal of JeffreySarnoff adresses practical use questions very well.

Concerning the difference to common understanding, there are open questions. I agree a formulation "stricter than in other languages" is too vague. What about comparing to wikipedia definition? It stresses two criteria: (1) welldefinedness (2) no side effects. From the former discussion I know (2) is the critical part. in particular concerning method tables. The proposal states very strictly "must use built-ins only, no generics". This restriction assures safety of @pure use, but is too restrictive: all @pure annotated functions in julia base call generic functions (I verified that on julia 1.5.3 source).

My impression is: it is very difficult to formulate the exact preconditions on calls of generic functions in @pure annotated functions. Hiere is my draft for the chapter on generic functions (feel free to modify/correct):

  • calling built-in functions in a @pure function is permitted, e.g.
 julia> tuple                             #  ok to use inside an `@pure` function
 tuple (built-in function)
  • calling other functions in a @pure function is critical. They are called generic functions and use global state (they dispatch through method tables). @pure changes the way julia compiler processes method tables of functions referenced in a @pure context - sorry, details on it are beyond scope of this doc (and subject to change in future julia releases). A sufficient condition for safe use is that a called function is also pure. This implies not only that the concrete method which is called in the @pure function has no side effects and always returns identical results on identical parameters, the method tables itself must not change: no redefinition of existing methods, no definition of new methods (but you cannot grant that: third party code can change any generic function in julia).
 julia> length                           # use inside a `@pure` function is at risk
 length (generic function)                  

@henriquebecker91
Copy link
Contributor Author

@JeffreySarnoff I have no problem in changing the text to what you have suggested, but I really think we need to address the inconsistent usage of @pure in Base issue. As @rryi pointed out, Base does not follow the no generic functions rule anywhere. Anybody that go search for official examples of @pure usage will get confused, it seems clear that the rule cannot be simply no generic functions unless there is something special about the Base module that we are not aware of.

Another doubt of mine, cannot you use a @pure function inside another @pure function?

@JeffreySarnoff
Copy link
Contributor

JeffreySarnoff commented Mar 9, 2021

Given two hyperpure functions, one could be used within the other.
Here is a silly example:

@pure field_names(nt::NamedTuple{N,T}) where {N,T} = N
@pure function duplicate_final_fieldname(nt::NamedTuple{N,T}) where {N,T}
    names = field_names(nt)
    names = (names..., last(names))
    return names
end

[note:
field_names expects a constructed NamedTuple
Base.fieldnames expects a NamedTuple Type
fieldnames is not declared pure in Base .. so far the only reason I have found for withholding pure from it
is that one can specify an illegal NamedTuple type and use that as an argument -- causing an error to be thrown.
Removing that error check turns the error into a no such method error, otherwise all is as it would be.

julia> const NT = NamedTuple{Tuple}
NamedTuple{Tuple, T} where T<:Tuple

julia> fieldnames(NT)
ERROR: ArgumentError: type does not have a definite number of fields
Stacktrace:
 [1] fieldcount(t::Any)     

the function Base.fieldnames(Type{NamedTuple}) is not declared to be pure

Here is a Base function annotated @pure that clearly violates the "requirements" reported above for all pure functions.

1 @pure function Rational{T}(x::AbstractIrrational) where T<:Integer
2    o = precision(BigFloat)
3    p = 256
4    while true
5      setprecision(BigFloat, p)
6      bx = BigFloat(x)
7      r = rationalize(T, bx, tol=0)
8       if abs(BigFloat(r) - bx) > eps(bx)
9          setprecision(BigFloat, o)
A            return r
B        end
C        p += 32
D    end
E  end

on line 2 the logic reaches outside of the edges of the function itself to bring inside its operation the current value of an external variable, a value that this function itself may alter as an application runs (see next).

on lines 5 and 9 the logic pushes its internal state outside of itself to update an external precision.

on line 7 there is a call to a nonpure external method

@JeffreySarnoff
Copy link
Contributor

JeffreySarnoff commented Mar 9, 2021

additional tidbits

The most deeply woven pure functions are those that the compiler knows to be pure as soon as it wakes up.

  • Core.Compiler._PURE_BUILTINS

    • :(===)(x, y)
    • nfields(x
    • tuple(xs)
    • Core.svec(xs)
  • ispuretopfunction

    • isbits(x)
    • isbitstype(T)
    • typejoin(T1, T2)
    • promote_type(T1, T2)

(from ast.md):

(a) any function known to be a pure function of its arguments
without respect to the state of the method caches or other
mutable global state.

(b) [struct MethodResultPure is used to] represent [that] a method result constant
was proven to be effect-free, including being no-throw (typically because
the value was computed by calling an @pure function)._

pure functions inside namedtuples.jl

@pure function merge_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}})
    @nospecialize an bn
    names = Symbol[an...]
    for n in bn
        if !sym_in(n, an)
            push!(names, n)
        end
    end
    (names...,)
end

@pure function merge_types(names::Tuple{Vararg{Symbol}}, a::Type{<:NamedTuple}, b::Type{<:NamedTuple})
    @nospecialize names a b
    bn = _nt_names(b)
    return Tuple{Any[ fieldtype(sym_in(names[n], bn) ? b : a, names[n]) for n in 1:length(names) ]...}
end

@pure function diff_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}})
    @nospecialize an bn
    names = Symbol[]
    for n in an
        if !sym_in(n, bn)
            push!(names, n)
        end
    end
    (names...,)
end

@henriquebecker91
Copy link
Contributor Author

I have read this old discussion on the purity of Rational but to me it does not seem to reach a clear conclusion. Is it a bug that Rational ignores future changes to setprecision because of @pure? Was it intended, i.e., the idea was that setprecision should be called before any call to Rational and then never after? Should we open a PR removing @pure annotations from the Base because they are, in fact, wrongly used?

@JeffreySarnoff
Copy link
Contributor

JeffreySarnoff commented Mar 9, 2021

Well -- @vtjnash is this (and other) season's final arbiter of which uses of @pure are "wrong", are "satisfactory", or are "brilliant".

I have collected all appearances of @pure that I found in Julia's github latest. There could be some opportunity to prune and replant more cohesively -- otoh, what was declared pure was done purposefully.

I will clusterify those uses so we have a place from which to take the long view.

@kimikage

This comment has been minimized.

@JeffreySarnoff
Copy link
Contributor

JeffreySarnoff commented Mar 10, 2021

I find it easier to start with constructive descriptions --

Are there specific uses or recognized situations in which use of these macros is encouraged?

Looking around Base, it appears that functions using @pure / @_pure_meta [often] also use @nospecialize for their arguments. Are their circumstances where using @pure/@_pure_meta is appropriate but leaving the arguments alone is the better choice?

(gentle segue)
What is gained by purifying function Rational{T}(x::AbstractIrrational)? How often do projects ask for Rational{Int}(pi) over and over again within the same run? If the answer is not many, and there is no other strong demand for this extra rapidity ... Or is this a situation where @pure is used for the boost to other, down the line, rational functions .. Or is the use of @pure to prevent the compiler from some misstep?

The functions that are declared pure and also may set and adjust the precision for BigFloats are some of the least easily explained exceptions -- they walk all over external state.

@rryi
Copy link

rryi commented Mar 12, 2021

@JeffreySarnoff

once a pure function has been called, it cannot be extended to support another signature

What does "extended" mean here?
In terms of your simple (but really not so silly) example:

nt1 = (a=1,)                                           # creates type NamedTuple{(:a,), Tuple{Int64}}
@pure field_names(nt::NamedTuple{N,T}) where {N,T} = N # function declaration
n1 = field_names(nt1)                                  # first call of pure function, compiler puts this in its method table:
                                                       # field_names(::NamedTuple{(:a,), Tuple{Int64}})
nt1b = (a=2,)                                          # re-use of existing NT type
n1b = field_names(nt1b)                                # no problem. Uses already existing method

nt2 =  (a=0x1,b="2nd NT type")                         # creates type NamedTuple{(:a, :b), Tuple{UInt8, String}}
n2 =  field_names(nt2)                                 # Compiler needs new method, compiles and adds method to method table
                                                       # signature field_name(::NamedTuple{(:a, :b), Tuple{UInt8, String}})

If code above is a counterexample demonstrating invalid use of @pure, I think the consequence is that a @pure function must not be defined with free type parameters in its signature like N, T in the example.

Maybe @nospecialize could cure that: my understanding is that @nospecialize tells the compiler not to generate methods with concrete types in its signature everytime it encounters a new signature of concrete types, but to compile exactly one method which has exactly the signature with abstract types and restrictions as stated in the function definition, at the price of adding type information to actual parameters in every call of the function at runtime.

@JeffreySarnoff
Copy link
Contributor

JeffreySarnoff commented Mar 12, 2021

Strictly speaking, [almost] none of the "rules" is a rule. Until we have completed this foray to know what's what, our proposed guidelines for effective and safe assertions of functional purity is virtually unreadable. For the moment, we are better served by considering current guidance as best-practice to avoid bad practice.

There is no requirement that breaking current guidance must cause havoc, nor that havoc caused be havoc revealed.

  • (either) Rather than see your example as a counter-example, I would consider it a violation of the guidance.
  • (or else) Rather than see your example as an edge case, I would consider it worth documenting as permissible.

for the docs:

Some strongly disreccomended uses of @pure may not cause trouble. This does not mean misuse of @pure is ok.
It is rather difficult to follow all possible dataflows that may be modified if @pure used in ways that violate guidance.
To further ascertain that no issue could possibly arise from that is not a good use of your time nor of other's resources.

@kimikage
Copy link
Contributor

kimikage commented Mar 13, 2021

In the first place, I think the (pre-)requirements for applying @pure are self-contradictory. The typical reason for using @pure is to improve inference and constant propagation. In such a case, if there is no @pure, it means that the result of function may depend on the global state.

In other words, the documentation has two purposes: to prevent inadvertent misuse of @pure, and to provide guidelines for those who want to use @pure at their own risk. It is desirable that both goals are achieved, but it would be good if one or the other could be clarified first. I think we are confused by trying to achieve both goals at the same time.

Edit:
To be more specific, I don't think it's practical to specify both in the allow-list approach. The compiler is steadily getting smarter, and we may be able to reduce the unsafe use of @pure in Base and stdlibs. However, some packages still need to support older compilers. If @pure is already in use, it is more practical to address the specific risks.

@JeffreySarnoff
Copy link
Contributor

thank you for cutting through the foliage. Your point is well taken -- we must do both, and we certainly may do one and then the other. Our interaction has lead us to ask better questions. Respecting the process, let's ask them.

@vtjnash we have better questions and an improved strategy.

we understand the rewrite of the inline docs, when completed, should include (a), (b), (c) (maybe (d), tbd).

(a) clear and direct guidance that protects the Julia's runtime integrity from a developer's misunderstanding.

(b) tighter, more pithy hand-holding for developers who would use @pure where the context is on the nice list
and the purpose for its use is one recognized as legitimate.

(c) When the following occur and is likely fixable with the use of @pure to "pull the compiler's attention into better focus".. to explain how to recognize or how to test for this circumstance. The to show how to apply the fix (use the macro as it is designed to be used) and advise best practice to ascertain this modification provided improvement.

for each of (a), (b), (c), we would benefit from hearing some specific characterizations and qualitative details that apply

Looking at pure functions within Base (except for (c) uses), it seems purity is best provided with no_specialization.
If I read the flow correctly, declaring a function pure allows the compiler to forgo some of its usual light-hearted
work, then there is available (maybe provisionable) advantage.. which may be realized as extra alacrity aor suffused
through further fast-lane-ing these evolving computational entities. Yes? No? Sometimes? Only on weekends?

Some of Base's pure functions engage in boundary hopping through their use of BigFloats, the possibly repeated use
of precision while setprecision may be used to change the BigFloat precision. In these situations, a call to the same
function (here precision [the getter]) with the same argument (here BigFloat) could return different values while
the work of a single invocation continues. With the same activity, there is some stomping around in external memory
as BigFloat values are modified. The participants are unsure of what to regard as appropriate with these uses.
Is this actually more sensible than it sounds? Has this developed as a matter of necessity?
What does it say about respecting the same constraints when given as guidance?

Have you been considering other ways to modify guidance about avoiding the use of pure functions?
Have you been considering other ways to modify guidance that facilitates successful use of pure functions?

@tpapp
Copy link
Contributor

tpapp commented Mar 18, 2021

I am wondering what the practical utility of documenting Base.@pure is, given that it is explicitly stated that

This macro is intended for internal compiler use and may be subject to changes.

In other words, you should not be using it in a package unless you follow the development of the language very closely to catch changes, and/or are willing pin to a specific version of Julia. It is documented to a sufficient level already for internal use.

@henriquebecker91
Copy link
Contributor Author

It is documented to a sufficient level already for internal use.

I think this is provably false: basically all uses inside Base break the no generic functions guideline, this PR has brought up many information not contained in the documentation, and other core developers ask Jameson if they can use @pure instead checking the documentation of @pure.

@henriquebecker91
Copy link
Contributor Author

I have to admit, however, that I feel like the text should start with a disclaimer saying if you use it outside of Base you void all warranties. And then documentation that help the bold and/or the core developers to use it correctly.

@tpapp
Copy link
Contributor

tpapp commented Mar 18, 2021

basically all uses inside Base break the no generic functions guideline

I am not so sure about this — quite a few of them seem valid (there are around 20 in total in base/ in any case).

I am wondering if in fact @pure is actually needed in all cases, given various recent compiler improvements. But these should be revisited on an individual bases (something similar to #32427).

@henriquebecker91
Copy link
Contributor Author

I am not so sure about this — quite a few of them seem valid (there are around 20 in total in base/ in any case).

Well, you can point them to @rryi, that said above:

The proposal states very strictly "must use built-ins only, no generics". This restriction assures safety of @pure use, but is too restrictive: all @pure annotated functions in julia base call generic functions (I verified that on julia 1.5.3 source).

The emphasis on "all" is not mine.

@KristofferC
Copy link
Sponsor Member

KristofferC commented Mar 18, 2021

To be fair, the current documentation was not added by one of the persons that regularly work on the compiler. Which kind of proves the point, adding a bunch of docs to this part of the compiler internals demonstrably causes more confusion than assistance.

Personally, I think the current docs should just be removed since it is wrong, and wrong docs are worse than no docs.

Edit: #40092

@tpapp
Copy link
Contributor

tpapp commented Mar 18, 2021

Simple grepping for @pure in base/ (current master) shows examples like

Base.jl:148:@pure sizeof(s::String) = Core.sizeof(s)
strings/string.jl:98:@pure ncodeunits(s::String) = Core.sizeof(s)

which I guess should be OK.

In any case, if a usage of @pure is incorrect in Base, one should just open an issue (or a PR, ideally).

But I think that the sufficient conditions enumerated above are too strong — some code in base/ just assumes that no one will pirate basic methods like push! for core types, and if that happens then hell breaks lose anyway. This is the kind of consistency you can assume in Base, but not in a package.

(incidentally, please let's try to quote @pure, instead of pinging the user with this nick)

@henriquebecker91
Copy link
Contributor Author

If we deem that is impracticable to give someone without years of experience in the compiler a short/clean guideline on how to use @pure (either because it needs pages of text or because the compiler changes faster than the docs) we can adopt an alternative path:

  1. Change the documentation to something in the lines "Do not use. For internal compiler use only. Using this macro outside of the Julia compiler will void all guarantees and warranties. Do not ask in the forums if your usage of @pure is okay."
  2. Keep all knowledge about @pure in the comments above the macro, for the benefit of the core developers, and people that have dived deep to try to understand @pure. This will have no documentation power, however, and it cannot be held accountable (i.e., it may be outdated).

@henriquebecker91
Copy link
Contributor Author

Simple grepping for @pure in base/ (current master) shows examples like

Base.jl:148:@pure sizeof(s::String) = Core.sizeof(s)
strings/string.jl:98:@pure ncodeunits(s::String) = Core.sizeof(s)

@rryi probably has confused Core.sizeof and Base.sizeof when stated that all @pure functions in base use generic functions. This does not change that most of them do. And, following your suggestion, we would need to open PRs for almost all of them if the guideline is kept as it is.

some code in base/ just assumes that no one will pirate basic methods like push! for core types, and if that happens then hell breaks lose anyway.

If this is the case, this is, @pure only restricts the use of generic functions if they can end up be extended for more specific types than the ones at the moment of the definition, then we could just write this in the documentation. It is not so long. I will probably need to take some time this weekend to make a new draft of the documentation. But the problem I face here is that I myself do not work in the compiler internals, so I can just piece the info people gave me and wait to see if the reviewers agree that it is correct.

@KristofferC
Copy link
Sponsor Member

Change the documentation to something in the lines "Do not use. For internal compiler use only.

It already says it is for internal compiler use. #40092 gets rid of all the guessing.

@henriquebecker91
Copy link
Contributor Author

I guess I will wait to see what the core developers think it is the best route before putting more effort on this PR. Keeping it documented enough just for nobody outside the compiler development to use it, or trying to give a usage guideline.

@Keno
Copy link
Member

Keno commented Mar 18, 2021

I think the truth of the matter is that there are very few uses of @pure that are both sound and useful. It is there as an escape hatch for when the compiler is unable to prove the purity of something, but purity is required. It is probably best thought of as a TODO note for improving the compiler or code in question, while punting the exact extent of that to later.

@JeffreySarnoff
Copy link
Contributor

JeffreySarnoff commented Mar 18, 2021

and for when the compiler is inferring (or deferring) incorrectly where the insertion of @pure sets it straight (paraphrasing @vtjnash: .. typically it should not be needed .. instead @pure is often used as a means of telling the compiler something it knows is false. This wasn't true a number of years ago, but when we encounter new cases, we tend to try to add them to the compiler ..)

@henriquebecker91
Copy link
Contributor Author

For people arriving at this thread now, the decision leaned in favor of "Keeping it documented enough just for nobody outside the compiler development to use it" and it was done in PR #40092; some annotations of pure are recently being removed by #40097. And it seems clear that the decision of marking a function as @pure, or not, is often confusing for own Core developers (#39792 (comment) is a recent example). The comments of Keno and Jeffrey above sum up the general feelings about @pure, that led to the decision it is better to keep it on the hands of the compiler developers.

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

Successfully merging this pull request may close these issues.