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

lowering: Refactor lowering for const and typed globals #54773

Merged
merged 1 commit into from
Jun 23, 2024

Conversation

Keno
Copy link
Member

@Keno Keno commented Jun 12, 2024

This is a prepratory commit for #54654 to change the lowering of const and typed globals to be compatible with the new semantics.

Currently, we lower const a::T = val to:

const a
global a::T
a = val

(which further expands to typed-globals an implicit converts).

This works, because, under the hood, our const declarations are actually assign-once globals. Note however, that this is not syntactically reachable, since we have a parse error for plain const a:

julia> const a
ERROR: ParseError:
# Error @ REPL[1]:1:1
const a
└─────┘ ── expected assignment after `const`
Stacktrace:
 [1] top-level scope
   @ none:1

However, this lowering is not atomic with respect to world age. The semantics in #54654 require that the const-ness and the value are established atomically (with respect to world age, potentially on another thread) or undergo invalidation.

To resolve this issue, this PR changes the lowering of const a::T = val to:

let
    local a::T = val
    const (global a) = a
end

where the latter is a special syntax form Expr(:const, GlobalRef(,:a), :a).

A similar change is made to const global declarations, which previously lowered via intrinsic, i.e. global a::T = val lowered to:

global a
Core.set_binding_type!(Main, :a, T)
_T = Core.get_binding_type(Main, :a)
if !isa(val, _T)
    val = convert(_T, val)
end
a = val

This changes the set_binding_type! to instead be a syntax form Expr(:globaldecl, :a, T). This is not technically required, but we currently do not use intrinsics for world-age affecting side-effects anywhere else in the system. In particular, after #54654, it would be illegal to call set_binding_type! in anything but top-level context. Now, we have discussed in the past that there should potentially be intrinsic functions for global modifications (method table additions, etc), currently only reachable through Core.eval, but such an intrinsic would require semantics that differ from both the current set_binding_type! and the new :globaldecl. Using an Expr form here is the most consistent with our current practice for these sort of things elsewhere and accordingly, this PR removes the intrinsic.

Note that this PR does not yet change any syntax semantics, although there could in principle be a reordering of side-effects within an expression (e.g. things like global a::(@isdefined(a) ? Int : Float64) might behave differently after this commit. However, we never defined the order of side effects (which is part of what this is cleaning up, although, I am not formally defining any specific ordering here either - #54654 will do some of that), and that is not a common case, so this PR should be largely considered non-semantic with respect to the syntax change.

Also fixes #54787 while we're at it.

@Keno Keno requested a review from JeffBezanson June 12, 2024 07:59
@Keno Keno added the DO NOT MERGE Do not merge this PR! label Jun 12, 2024
@Keno
Copy link
Member Author

Keno commented Jun 12, 2024

DNM, because the base PR should be merged first.

@Keno Keno added the compiler:lowering Syntax lowering (compiler front end, 2nd stage) label Jun 12, 2024
@Keno Keno mentioned this pull request Jun 12, 2024
14 tasks
@vtjnash
Copy link
Member

vtjnash commented Jun 12, 2024

This is not technically required, but we currently do not use intrinsics for world-age affecting side-effects anywhere else in the system

Not entirely true, since loading a module is a function call, but has a world-age change side effect. Similarly, deleting a method is a function call, but has a world-age change side effect. So 2 out of 3 world-change side effects I can think of are function calls, and only adding a method is currently a syntactic-only form.

@Keno
Copy link
Member Author

Keno commented Jun 12, 2024

Not entirely true, since loading a module is a function call, but has a world-age change side effect. Similarly, deleting a method is a function call, but has a world-age change side effect. So 2 out of 3 world-change side effects I can think of are function calls, and only adding a method is currently a syntactic-only form.

I meant world-age affecting syntax forms, which is currently :method and :using. The issue here is that we may potentially need to be able to increment the world age within a block of code in order to make new globals visible, which is not something that functions are semantically allowed to do currently.

@Keno Keno force-pushed the kf/constgloballowerrefactor branch from a6b3ce0 to 17320fa Compare June 12, 2024 20:37
Base automatically changed from kf/rmouterref to master June 12, 2024 21:45
@vtjnash
Copy link
Member

vtjnash commented Jun 12, 2024

Minor nit: currently :using does not introduce a world-age change (though after #54654 it would have to)

potentially need to be able to increment the world age within a block of code in order to make new globals visible, which is not something that functions are semantically allowed to do currently

When? Currently functions are not allowed to observe new worlds, while global scope does so, but it also does update the world within a block of code to make it permitted

@Keno
Copy link
Member Author

Keno commented Jun 13, 2024

Fair enough, the world age update at global scope is finer than I had thought (I thought it captured it at the entry to thunk evaluation, but it actually updates it every statement). I'm happy to go the other way and make both of these lower to intrinsics instead. Would you prefer that?

@vtjnash
Copy link
Member

vtjnash commented Jun 13, 2024

Eventually, I think that will be desirable, though it is unimportant for the PR since that is a new feature it can be added later without breaking the syntactic form

@Keno
Copy link
Member Author

Keno commented Jun 13, 2024

Alright, I'll leave it as is then and tweak the commit message.

@Keno Keno force-pushed the kf/constgloballowerrefactor branch from 17320fa to 8816e4f Compare June 13, 2024 05:48
@Keno Keno added needs docs Documentation for this change is required needs pkgeval Tests for all registered packages should be run with this change and removed DO NOT MERGE Do not merge this PR! needs docs Documentation for this change is required labels Jun 13, 2024
@Keno Keno force-pushed the kf/constgloballowerrefactor branch 3 times, most recently from df97a48 to dfaa660 Compare June 14, 2024 06:40
@Keno
Copy link
Member Author

Keno commented Jun 14, 2024

@nanosoldier runtests()

@nanosoldier
Copy link
Collaborator

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

@Keno
Copy link
Member Author

Keno commented Jun 14, 2024

Fixed the const a = b = ... issue to make it work, but also filed JuliaSIMD/VectorizationBase.jl#113 to fix the confusion in the package.

@Keno
Copy link
Member Author

Keno commented Jun 14, 2024

@nanosoldier runtests(["TestEnv", "EulerAngles", "UnitSystems", "MuttsInterface", "Losers", "Monadic", "Geophysics", "Wrangling", "VPTrees", "RDates", "HMMGradients", "SubSIt", "HolidayCalendars", "Similitude", "BlockMatching", "Mosquitto", "MaskArrays", "BitcoinPrimitives", "GraphicsMath", "Mueller", "UnitfulCurrency", "ParaReal", "HMatrices", "TiledIteration", "UnitfulAssets", "OutlierDetectionTrees", "ExponentialUtilities", "Discreet", "SubpixelRegistration", "OpticalPropagation", "AccurateArithmetic", "SIMDMathFunctions", "ImplicitPlots", "PAndQ", "StatProfilerHTML", "ImageShow", "TypeClasses", "AMGCLWrap", "ExtensibleEffects", "FiniteDiff", "DynamicPolynomials", "LoopManagers", "ExtremeLearning", "Hecke", "Sixel", "TimeseriesFeatures", "GPLinearODEMaker", "Keldysh", "PreprocessMD", "Tensorial", "RecursiveArrayTools", "ImageInTerminal", "KeldyshED", "ExtendableSparse", "Dolang", "CodingTheory", "PreallocationTools", "QuantumPropagators", "ImageSmooth", "LiiBRA", "QuantumControl", "MathJaxRenderer", "SpectralResampling", "ImageFiltering", "FastDMTransform", "GIFImages", "LabelledArrays", "MarsagliaDiscreteSamplers", "FastHistograms", "BoxLeastSquares", "DelayEmbeddings", "VectorizedStatistics", "TriangularSolve", "MutualInformationImageRegistration", "FinanceCore", "SparseExtra", "NLLSsolver", "TropicalGEMM", "CategoricalMonteCarlo", "VectorizedReduction", "SpheriCart", "ObjectPools", "RegressionTables", "MRICoilSensitivities", "RainFARM", "AlgebraicSolving", "Gaius", "ImageIO", "MLJScientificTypes", "Powerful", "LibRaw", "SmoQyDEAC", "RollingFunctions", "StatisticalProcessMonitoring", "RandomWalkBVP", "ElectromagneticFields", "FrankWolfe", "Determinantal", "VisualGeometryOptimization", "ImageDistances", "STREAMBenchmark", "OptimizationPRIMA", "FastGeoProjections", "ComputerVisionMetrics", "SpatialAccessTrees", "QSM", "Miter", "RegularizedLeastSquares", "MLJLinearModels", "Gtk", "MicroscopePSFs", "DoseCalculators", "TwoDots", "LocalPoly", "GtkSourceWidget", "ChebParticleMesh", "LifeContingencies", "SimpleDiffEq", "FinEtoolsHeatDiff", "FinEtoolsAcoustics", "ModiaResult", "LSODA", "ArDCA", "ModiaBase", "FinEtoolsDeforLinear", "FinEtoolsFlexStructures", "FinEtoolsMeshing", "Jadex", "ODEInterfaceDiffEq", "RedClust", "FinEtoolsMultithreading", "AbstractCosmologicalEmulators", "PSSFSS", "GLFixedEffectModels", "ImageQualityIndexes", "StatsLearnModels", "DECAES", "EmpiricalPotentials", "CalciumScoring", "VisualRegressionTests", "Sundials", "SpeedMapping", "DrugInteractions", "SimSearchManifoldLearning", "Polynomials4ML", "AlgebraicMultigrid", "ImageSegmentation", "StartUpDG", "ProfileView", "EconomicScenarioGenerators", "CategoryData", "MPIMeasurements", "BoundaryValueProblems", "SignalTablesInterface_PyPlot", "PsychomotorVigilanceTask", "ModiaPlot_PyPlot", "TwoDotsModels", "OptimizationOptimJL", "MCMCChains", "PolaronMobility", "SurveyDataWeighting", "Yields", "SphericalFunctions", "RandomFeatures", "MaximumEntropyMomentClosures", "MRIReco", "TruncatedMVN", "DiffEqFinancial", "SpikeSorting", "MLJParticleSwarmOptimization", "MLJSerialization", "DoloYAML", "QuantitativeSusceptibilityMappingTGV", "Unfolding", "ROMEO", "OptimizationSpeedMapping", "ApproxMasterEqs", "Dolo", "MriResearchTools", "SimSpin", "GlobalSensitivity", "CLEARSWI", "IRKGaussLegendre", "SensitivityRankCondition", "GMMParameterEstimation", "ActuaryUtilities", "FourierGPE", "ITensorMPS", "LowLevelParticleFilters", "PredefinedDynamicalSystems", "RealPolyhedralHomotopy", "SchwarzChristoffel", "QuasiCopula", "GeoEnergyIO", "ImageQuilting", "GeneralizedSDistributions", "Jutul", "EMpht", "BattMo", "FMIBase", "StanModels", "DecomposingPolynomialSystems", "Images", "ColorSchemeTools", "Eikonal", "DIVAnd_HFRadar", "FSimBase", "ImageTracking", "ImageFeatures", "Consensus", "TrajGWAS", "FSimZoo", "AiidaDFTK", "OrdinalGWAS", "StatisticalRethinking", "FrequencySweep", "GpABC", "SpiDy", "ChaosTools", "LongwaveModePropagator", "GameTheory", "ControlSystemIdentification", "Plots", "SurfaceReactions", "GeoStatsTransforms", "CausalityTools", "LowRankIntegrators", "IndependentComponentAnalysis", "SciMLExpectations", "SurfaceCoverage", "StirredReactor", "BatchReactor", "HetaSimulator", "Attractors", "PlugFlowReactor", "BLASBenchmarksCPU", "SimGBS", "ODEProblemLibrary", "KiteModels", "FinEtoolsVibInFluids", "Knockoffs", "GeneralizedSasakiNakamura", "BaseModelica", "GalacticPotentials", "PolynomialGTM", "PiecewiseDeterministicMarkovProcesses", "Ai4EComponentLib", "KiteControllers", "Petri", "JointSurvivalModels", "JumpProblemLibrary", "SMLMData", "ProcessBasedModelling", "Circuitscape", "Omniscape", "NeuronBuilder", "FaultTolerantControl", "AstrodynamicalModels", "ConceptualClimateModels", "MomentClosure", "SMLMMetrics", "WGPUgfx", "ReactionNetworkImporters", "ONSAS", "SDEProblemLibrary", "GtkUtilities", "PGFPlots", "DiffEqProblemLibrary", "MinimallyDisruptiveCurves", "CellMLToolkit", "CellularPotts", "ClimateTools", "NumCME", "SMLMFrameConnection", "SIAN", "DataDrivenDiffEq", "OptimizationMOI", "EditorsRepo", "ClimatePlots", "ReactionSensitivity", "HmtArchive", "BasicAkerRelationalScore", "CollectiveSpins", "ModiaPlot_CairoMakie", "CirculatorySystemModels", "WaveletsExt", "MPIReco", "PortfolioAnalytics", "MathepiaModels", "ChargeTransport", "StructuredLight", "MendelImpute", "AtmosphericDeposition", "FSimPlots", "QuantumDynamics", "Biofilm", "MiseEnPage", "IonSim", "StateSpacePartitions", "MRINavigator", "WorldDynamics", "FourLeafMLE", "Pioran", "Chamber", "DiffusionGarnet", "SMLMSim", "OVERT", "RetentionParameterEstimator", "GasChromatographySimulator", "NeuroAnalysis", "HarmonicBalance", "QuantumAnnealingAnalytics"])

@nanosoldier
Copy link
Collaborator

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

@Keno Keno force-pushed the kf/constgloballowerrefactor branch from 717283e to 499eed2 Compare June 19, 2024 00:47
@Keno
Copy link
Member Author

Keno commented Jun 19, 2024

@nanosoldier runtests(["Geophysics", "GraphicsMath", "Similitude", "UnitSystems", "Mueller", "MuttsInterface", "OpticalPropagation", "LiiBRA", "UnitfulAssets", "GtkSourceWidget", "DoseCalculators", "TwoDots", "Gtk", "Miter", "Tensorial", "DrugInteractions", "PsychomotorVigilanceTask", "TwoDotsModels", "SpikeSorting", "Hecke", "ProfileView", "MPIMeasurements", "VisualRegressionTests", "DECAES", "RegressionTables", "MRIReco", "GtkUtilities", "LowRankIntegrators", "LongwaveModePropagator", "SpiDy", "FrequencySweep", "ReactionNetworkImporters", "CellMLToolkit", "ConceptualClimateModels", "WorldDynamics", "AtmosphericDeposition", "DiffusionGarnet", "QuantumAnnealingAnalytics"])

Comment on lines -2552 to -2555
!!! compat "Julia 1.9"
This function requires Julia 1.9 or later.
"""
Core.set_binding_type!
Copy link
Member

Choose a reason for hiding this comment

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

This is definitively a breaking change, as it removes a documented API present since v1.9, no?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's neither exported nor public - I think it is rightly considered an implementation detail of the syntax. In addition, I didn't find any public package that actually uses this any other way, so providing a deprecation implementation seems gratuitous.

@nanosoldier
Copy link
Collaborator

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

This is a prepratory commit for #54654 to change the lowering of
`const` and typed globals to be compatible with the new semantics.

Currently, we lower `const a::T = val` to:
```
const a
global a::T
a = val
```
(which further expands to typed-globals an implicit converts).

This works, because, under the hood, our const declarations
are actually assign-once globals. Note however, that this is
not syntactically reachable, since we have a parse error for
plain `const a`:

```
julia> const a
ERROR: ParseError:
# Error @ REPL[1]:1:1
const a
└─────┘ ── expected assignment after `const`
Stacktrace:
 [1] top-level scope
   @ none:1
```

However, this lowering is not atomic with respect to world age.
The semantics in #54654 require that the const-ness and the value
are established atomically (with respect to world age, potentially
on another thread) or undergo invalidation.

To resolve this issue, this PR changes the lowering of `const a::T = val` to:
```
let
    local a::T = val
    const (global a) = a
end
```
where the latter is a special syntax form `Expr(:const, GlobalRef(,:a), :a)`.

A similar change is made to const global declarations, which previously
lowered via intrinsic, i.e. `global a::T = val` lowered to:
```
global a
Core.set_binding_type!(Main, :a, T)
_T = Core.get_binding_type(Main, :a)
if !isa(val, _T)
    val = convert(_T, val)
end
a = val
```

This changes the `set_binding_type!` to instead be a syntax form `Expr(:globaldecl, :a, T)`.
This is not technically required, but we currently do not use intrinsics for
world-age affecting side-effects anywhere else in the system. In particular, after #54654,
it would be illegal to call `set_binding_type!` in anything but top-level context.
Now, we have discussed in the past that there should potentially be intrinsic functions
for global modifications (method table additions, etc), currently only reachable through
`Core.eval`, but such an intrinsic would require semantics that differ from both
the current `set_binding_type!` and the new `:globaldecl`.
Using an Expr form here is the most consistent with our current practice for
these sort of things elsewhere and accordingly, this PR removes the intrinsic.

Note that this PR does not yet change any syntax semantics,
although there could in principle be a reordering of
side-effects within an expression (e.g. things like
`global a::(@isdefined(a) ? Int : Float64)` might behave
differently after this commit. However, we never defined
the order of side effects (which is part of what this is
cleaning up, although, I am not formally defining any specific
ordering here either - #54654 will do some of that), and that
is not a common case, so this PR should be largely considered
non-semantic with respect to the syntax change.
@Keno Keno force-pushed the kf/constgloballowerrefactor branch from 499eed2 to f00f2e6 Compare June 19, 2024 23:33
@Keno
Copy link
Member Author

Keno commented Jun 19, 2024

@nanosoldier runtests(["Geophysics", "Similitude", "OpticalPropagation", "UnitSystems", "Mueller", "MuttsInterface", "LiiBRA", "UnitfulAssets", "Tensorial"])

@nanosoldier
Copy link
Collaborator

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

@Keno
Copy link
Member Author

Keno commented Jun 20, 2024

Remaining pkgeval failure is const Å = Å = angstrom in an old version of unitful. This happens to work right now on master because the const is declared and then the assignment to the first global Å is mistaken for setting the const (because the forms are not distinguished). We already decided that const doesn't distribute over , this actually just ambiguous. A different linearization on master would also error. I'm planning to just leave that as is, esp since this is already fixed on unitful master. Does once again reinforce though that two = in a single expression without parens doesn't mean what people think it does.

@Keno Keno removed the needs pkgeval Tests for all registered packages should be run with this change label Jun 20, 2024
# `const` does not distribute over assignments
const aconstassign = bconstassign = 2
@test isconst(@__MODULE__, :aconstassign)
@test !isconst(@__MODULE__, :bconstassign)
Copy link
Member

Choose a reason for hiding this comment

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

This might be a bug? (no need to fix in this PR, though)

@Keno Keno merged commit dfd1d49 into master Jun 23, 2024
8 of 9 checks passed
@Keno Keno deleted the kf/constgloballowerrefactor branch June 23, 2024 13:57
aviatesk added a commit that referenced this pull request Jun 26, 2024
Following up #54773.
Since top-level thunks including `:const` expressions are generally not
optimized, they are not validated either, which can cause problems for
interpreters like `REPLInterpreter` that perform inference on arbitrary
top-level chunks.
Keno added a commit that referenced this pull request Jul 2, 2024
This is a follow up to resolve a TODO left in #54773 as part of
preparatory work for #54654. Currently, our lowering for type
definition contains an early `isdefined` that forces a decision
on binding resolution before the assignment of the actual binding.
In the current implementation, this doesn't matter much, but
with #54654, this would incur a binding invalidation we would like
to avoid.

To get around this, we extend the (internal) `isdefined` form to take
an extra argument specifying whether or not to permit looking at
imported bindings. If not, resolving the binding is not required
semantically, but for the purposes of type definition (where assigning
to an imported binding would error anyway), this is all we need.
Keno added a commit that referenced this pull request Jul 2, 2024
This is a follow up to resolve a TODO left in #54773 as part of
preparatory work for #54654. Currently, our lowering for type
definition contains an early `isdefined` that forces a decision
on binding resolution before the assignment of the actual binding.
In the current implementation, this doesn't matter much, but
with #54654, this would incur a binding invalidation we would like
to avoid.

To get around this, we extend the (internal) `isdefined` form to take
an extra argument specifying whether or not to permit looking at
imported bindings. If not, resolving the binding is not required
semantically, but for the purposes of type definition (where assigning
to an imported binding would error anyway), this is all we need.
Keno added a commit that referenced this pull request Jul 2, 2024
This is a follow up to resolve a TODO left in #54773 as part of
preparatory work for #54654. Currently, our lowering for type
definition contains an early `isdefined` that forces a decision
on binding resolution before the assignment of the actual binding.
In the current implementation, this doesn't matter much, but
with #54654, this would incur a binding invalidation we would like
to avoid.

To get around this, we extend the (internal) `isdefined` form to take
an extra argument specifying whether or not to permit looking at
imported bindings. If not, resolving the binding is not required
semantically, but for the purposes of type definition (where assigning
to an imported binding would error anyway), this is all we need.
Keno added a commit that referenced this pull request Jul 2, 2024
This is a follow up to resolve a TODO left in #54773 as part of
preparatory work for #54654. Currently, our lowering for type
definition contains an early `isdefined` that forces a decision
on binding resolution before the assignment of the actual binding.
In the current implementation, this doesn't matter much, but
with #54654, this would incur a binding invalidation we would like
to avoid.

To get around this, we extend the (internal) `isdefined` form to take
an extra argument specifying whether or not to permit looking at
imported bindings. If not, resolving the binding is not required
semantically, but for the purposes of type definition (where assigning
to an imported binding would error anyway), this is all we need.
Keno added a commit that referenced this pull request Jul 4, 2024
This is a follow up to resolve a TODO left in #54773 as part of
preparatory work for #54654. Currently, our lowering for type
definition contains an early `isdefined` that forces a decision
on binding resolution before the assignment of the actual binding.
In the current implementation, this doesn't matter much, but
with #54654, this would incur a binding invalidation we would like
to avoid.

To get around this, we extend the (internal) `isdefined` form to take
an extra argument specifying whether or not to permit looking at
imported bindings. If not, resolving the binding is not required
semantically, but for the purposes of type definition (where assigning
to an imported binding would error anyway), this is all we need.
Keno added a commit that referenced this pull request Jul 5, 2024
This is a follow up to resolve a TODO left in #54773 as part of
preparatory work for #54654. Currently, our lowering for type
definition contains an early `isdefined` that forces a decision
on binding resolution before the assignment of the actual binding.
In the current implementation, this doesn't matter much, but
with #54654, this would incur a binding invalidation we would like
to avoid.

To get around this, we extend the (internal) `isdefined` form to take
an extra argument specifying whether or not to permit looking at
imported bindings. If not, resolving the binding is not required
semantically, but for the purposes of type definition (where assigning
to an imported binding would error anyway), this is all we need.
Keno added a commit that referenced this pull request Jul 6, 2024
This is a follow up to resolve a TODO left in #54773 as part of
preparatory work for #54654. Currently, our lowering for type definition
contains an early `isdefined` that forces a decision on binding
resolution before the assignment of the actual binding. In the current
implementation, this doesn't matter much, but with #54654, this would
incur a binding invalidation we would like to avoid.

To get around this, we extend the (internal) `isdefined` form to take an
extra argument specifying whether or not to permit looking at imported
bindings. If not, resolving the binding is not required semantically,
but for the purposes of type definition (where assigning to an imported
binding would error anyway), this is all we need.
@aviatesk
Copy link
Member

aviatesk commented Jul 9, 2024

Regarding the newly added :globaldecl, is it likely to be replaced by an intrinsic function in the near future? If not, I'm considering adding basic support in inference for an external abstract interpreter to run inference on top-level thunks.

@aviatesk
Copy link
Member

aviatesk commented Jul 9, 2024

Just a side note: the fact that the world-age of top-level thunks can be incremented with each statement might mean that new considerations are necessary for the correctness of inference on top-level chunks.

aviatesk added a commit that referenced this pull request Jul 16, 2024
Following up #54773.
Required for external abstract interpreters that may run inference on
arbitrary top-level thunks.
Keno pushed a commit that referenced this pull request Jul 16, 2024
Following up #54773.
Required for external abstract interpreters that may run inference on
arbitrary top-level thunks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:lowering Syntax lowering (compiler front end, 2nd stage)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

const before destructuring is inconsistent
5 participants