381
381
`@assume_effects` overrides the compiler's effect modeling for the given method.
382
382
`ex` must be a method definition or `@ccall` expression.
383
383
384
+ !!! compat "Julia 1.8"
385
+ Using `Base.@assume_effects` requires Julia version 1.8.
386
+
384
387
```jldoctest
385
388
julia> Base.@assume_effects :terminates_locally function pow(x)
386
389
# this :terminates_locally allows `pow` to be constant-folded
@@ -402,7 +405,7 @@ julia> code_typed() do
402
405
1 ─ return 479001600
403
406
) => Int64
404
407
405
- julia> Base.@assume_effects :total_may_throw @ccall jl_type_intersection(Vector{Int}::Any, Vector{<:Integer}::Any)::Any
408
+ julia> Base.@assume_effects :total !:nothrow @ccall jl_type_intersection(Vector{Int}::Any, Vector{<:Integer}::Any)::Any
406
409
Vector{Int64} (alias for Array{Int64, 1})
407
410
```
408
411
@@ -423,12 +426,15 @@ The following `setting`s are supported.
423
426
- `:nothrow`
424
427
- `:terminates_globally`
425
428
- `:terminates_locally`
429
+ - `:foldable`
426
430
- `:total`
427
431
432
+ # Extended help
433
+
428
434
---
429
- # `:consistent`
435
+ ## `:consistent`
430
436
431
- The `:consistent` setting asserts that for egal inputs:
437
+ The `:consistent` setting asserts that for egal (`===`) inputs:
432
438
- The manner of termination (return value, exception, non-termination) will always be the same.
433
439
- If the method returns, the results will always be egal.
434
440
@@ -461,7 +467,7 @@ The `:consistent` setting asserts that for egal inputs:
461
467
itself is not required to meet the egality requirement specified above.
462
468
463
469
---
464
- # `:effect_free`
470
+ ## `:effect_free`
465
471
466
472
The `:effect_free` setting asserts that the method is free of externally semantically
467
473
visible side effects. The following is an incomplete list of externally semantically
@@ -491,7 +497,7 @@ were not executed.
491
497
valid for all world ages and limit use of this assertion accordingly.
492
498
493
499
---
494
- # `:nothrow`
500
+ ## `:nothrow`
495
501
496
502
The `:nothrow` settings asserts that this method does not terminate abnormally
497
503
(i.e. will either always return a value or never return).
@@ -505,7 +511,7 @@ The `:nothrow` settings asserts that this method does not terminate abnormally
505
511
`MethodErrors` and similar exceptions count as abnormal termination.
506
512
507
513
---
508
- # `:terminates_globally`
514
+ ## `:terminates_globally`
509
515
510
516
The `:terminates_globally` settings asserts that this method will eventually terminate
511
517
(either normally or abnormally), i.e. does not loop indefinitely.
@@ -520,7 +526,7 @@ The `:terminates_globally` settings asserts that this method will eventually ter
520
526
on a method that *technically*, but not *practically*, terminates.
521
527
522
528
---
523
- # `:terminates_locally`
529
+ ## `:terminates_locally`
524
530
525
531
The `:terminates_locally` setting is like `:terminates_globally`, except that it only
526
532
applies to syntactic control flow *within* the annotated method. It is thus
@@ -531,59 +537,79 @@ non-termination if the method calls some other method that does not terminate.
531
537
`:terminates_globally` implies `:terminates_locally`.
532
538
533
539
---
534
- # `:total`
540
+ ## `:foldable`
541
+
542
+ This setting is a convenient shortcut for the set of effects that the compiler
543
+ requires to be guaranteed to constant fold a call at compile time. It is
544
+ currently equivalent to the following `setting`s:
535
545
536
- This `setting` combines the following other assertions:
537
546
- `:consistent`
538
547
- `:effect_free`
539
- - `:nothrow`
540
548
- `:terminates_globally`
541
- and is a convenient shortcut.
549
+
550
+ !!! note
551
+ This list in particular does not include `:nothrow`. The compiler will still
552
+ attempt constant propagation and note any thrown error at compile time. Note
553
+ however, that by the `:consistent`-cy requirements, any such annotated call
554
+ must consistently throw given the same argument values.
542
555
543
556
---
544
- # `:total_may_throw `
557
+ ## `:total `
545
558
546
- This `setting` combines the following other assertions:
559
+ This `setting` is the maximum possible set of effects. It currently implies
560
+ the following other `setting`s:
547
561
- `:consistent`
548
562
- `:effect_free`
563
+ - `:nothrow`
549
564
- `:terminates_globally`
550
- and is a convenient shortcut.
551
565
552
- !!! note
553
- This setting is particularly useful since it allows the compiler to evaluate a call of
554
- the applied method when all the call arguments are fully known to be constant, no matter
555
- if the call results in an error or not.
556
-
557
- `@assume_effects :total_may_throw` is similar to [`@pure`](@ref) with the primary
558
- distinction that the `:consistent`-cy requirement applies world-age wise rather
559
- than globally as described above. However, in particular, a method annotated
560
- `@pure` should always be `:total` or `:total_may_throw`.
561
- Another advantage is that effects introduced by `@assume_effects` are propagated to
562
- callers interprocedurally while a purity defined by `@pure` is not.
566
+ !!! warning
567
+ `:total` is a very strong assertion and will likely gain additional semantics
568
+ in future versions of Julia (e.g. if additional effects are added and included
569
+ in the definition of `:total`). As a result, it should be used with care.
570
+ Whenever possible, prefer to use the minimum possible set of specific effect
571
+ assertions required for a particular application. In cases where a large
572
+ number of effect overrides apply to a set of functions, a custom macro is
573
+ recommended over the use of `:total`.
574
+
575
+ ---
576
+ ## Negated effects
577
+
578
+ Effect names may be prefixed by `!` to indicate that the effect should be removed
579
+ from an earlier meta effect. For example, `:total !:nothrow` indicates that while
580
+ the call is generally total, it may however throw.
581
+
582
+ ---
583
+ ## Comparison to `@pure`
584
+
585
+ `@assume_effects :foldable` is similar to [`@pure`](@ref) with the primary
586
+ distinction that the `:consistent`-cy requirement applies world-age wise rather
587
+ than globally as described above. However, in particular, a method annotated
588
+ `@pure` should always be at least `:foldable`.
589
+ Another advantage is that effects introduced by `@assume_effects` are propagated to
590
+ callers interprocedurally while a purity defined by `@pure` is not.
563
591
"""
564
592
macro assume_effects (args... )
565
593
(consistent, effect_free, nothrow, terminates_globally, terminates_locally) =
566
594
(false , false , false , false , false , false )
567
- for setting in args[1 : end - 1 ]
568
- if isa (setting, QuoteNode)
569
- setting = setting. value
570
- end
595
+ for org_setting in args[1 : end - 1 ]
596
+ (setting, val) = compute_assumed_setting (org_setting)
571
597
if setting === :consistent
572
- consistent = true
598
+ consistent = val
573
599
elseif setting === :effect_free
574
- effect_free = true
600
+ effect_free = val
575
601
elseif setting === :nothrow
576
- nothrow = true
602
+ nothrow = val
577
603
elseif setting === :terminates_globally
578
- terminates_globally = true
604
+ terminates_globally = val
579
605
elseif setting === :terminates_locally
580
- terminates_locally = true
606
+ terminates_locally = val
607
+ elseif setting === :foldable
608
+ consistent = effect_free = terminates_globally = val
581
609
elseif setting === :total
582
- consistent = effect_free = nothrow = terminates_globally = true
583
- elseif setting === :total_may_throw
584
- consistent = effect_free = terminates_globally = true
610
+ consistent = effect_free = nothrow = terminates_globally = val
585
611
else
586
- throw (ArgumentError (" @assume_effects $setting not supported" ))
612
+ throw (ArgumentError (" @assume_effects $org_setting not supported" ))
587
613
end
588
614
end
589
615
ex = args[end ]
@@ -598,6 +624,16 @@ macro assume_effects(args...)
598
624
return esc (pushmeta! (ex, :purity , consistent, effect_free, nothrow, terminates_globally, terminates_locally))
599
625
end
600
626
627
+ function compute_assumed_setting (@nospecialize (setting), val:: Bool = true )
628
+ if isexpr (setting, :call ) && setting. args[1 ] === :(! )
629
+ return compute_assumed_setting (setting. args[2 ], ! val)
630
+ elseif isa (setting, QuoteNode)
631
+ return compute_assumed_setting (setting. value, val)
632
+ else
633
+ return (setting, val)
634
+ end
635
+ end
636
+
601
637
"""
602
638
@propagate_inbounds
603
639
0 commit comments