# This file is a part of Julia. License is MIT: https://julialang.org/license

using Test

include(joinpath(@__DIR__,"../Compiler/test/irutils.jl"))

# code_native / code_llvm (issue #8239)
# It's hard to really test these, but just running them should be
# sufficient to catch segfault bugs.

module ReflectionTest
using Test, Random

function test_ir_reflection(freflect, f, types)
    @test !isempty(freflect(f, types))
    nothing
end

function test_ir_reflection(freflect, argtypes)
    @test !isempty(freflect(argtypes))
    nothing
end

function test_bin_reflection(freflect, f, types)
    iob = IOBuffer()
    freflect(iob, f, types)
    str = String(take!(iob))
    @test !isempty(str)
    nothing
end

function test_code_reflection(freflect, f, types, tester)
    tester(freflect, f, types)
    tester(freflect, f, (types.parameters...,))
    tt = Base.signature_type(f, types)
    tester(freflect, tt)
    tester(freflect, (tt.parameters...,))
    nothing
end

function test_code_reflections(tester, freflect)
    test_code_reflection(freflect, occursin,
                         Tuple{Regex, AbstractString}, tester) # abstract type
    test_code_reflection(freflect, +, Tuple{Int, Int}, tester) # leaftype signature
    test_code_reflection(freflect, +,
                         Tuple{Array{Float32}, Array{Float32}}, tester) # incomplete types
    test_code_reflection(freflect, Module, Tuple{}, tester) # Module() constructor (transforms to call)
    test_code_reflection(freflect, Array{Int64}, Tuple{Array{Int32}}, tester) # with incomplete types
    test_code_reflection(freflect, muladd, Tuple{Float64, Float64, Float64}, tester)
end

test_code_reflections(test_ir_reflection, code_lowered)
test_code_reflections(test_ir_reflection, code_typed)
test_code_reflections(test_ir_reflection, Base.code_ircode)

io = IOBuffer()
Base.print_statement_costs(io, map, (typeof(sqrt), Tuple{Int}))
str = String(take!(io))
@test occursin("map(f, t::Tuple{Any})", str)
@test occursin("sitofp", str)
@test occursin(r"20 .*sqrt_llvm.*::Float64", str)

end # module ReflectionTest

# code_llvm llvm_options parameter tests
@testset "code_llvm llvm_options" begin
    using InteractiveUtils: code_llvm

    # Test that llvm_options parameter works without crashing
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="")
    @test !isempty(String(take!(io)))

    # Test print-after-all produces IR dump output
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-after-all")
    output = String(take!(io))
    @test occursin("IR Dump After", output)
    @test occursin("define", output)  # Final IR should also be present

    # Test print-after with specific pass name
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-after=AfterOptimization")
    output = String(take!(io))
    @test occursin("IR Dump After", output)
    @test occursin("AfterOptimization", output)

    # Test print-before-all
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-before-all")
    output = String(take!(io))
    @test occursin("IR Dump Before", output)

    # Test print-module-scope shows module structure
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-after=AfterOptimization -print-module-scope")
    output = String(take!(io))
    @test occursin("ModuleID", output) || occursin("source_filename", output)

    # Test comma-separated pass names
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-after=BeforeOptimization,AfterOptimization")
    output = String(take!(io))
    @test occursin("IR Dump After BeforeOptimizationMarkerPass", output)
    @test occursin("IR Dump After AfterOptimizationMarkerPass", output)

    # Test repeated flags accumulate
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-after=BeforeOptimization -print-after=AfterOptimization")
    output = String(take!(io))
    @test occursin("IR Dump After BeforeOptimizationMarkerPass", output)
    @test occursin("IR Dump After AfterOptimizationMarkerPass", output)

    # Test unknown option warning appears in output
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-unknown-flag")
    output = String(take!(io))
    @test occursin("Warning: unknown llvm_options flag", output)

    # Test missing value warning appears in output
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-after")
    output = String(take!(io))
    @test occursin("Warning: -print-after requires a value", output)

    # Test filter-print-funcs with non-matching names suppresses all output
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-after-all -filter-print-funcs=julia_doesnotexist1,julia_doesnotexist2")
    output = String(take!(io))
    @test !occursin("IR Dump After", output)

    # Test filter-print-funcs with comma-separated list matches multiple functions
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-after-all -filter-print-funcs=julia_")
    output_one = String(take!(io))
    dumps_one = count("IR Dump After", output_one)
    io = IOBuffer()
    code_llvm(io, +, (Int, Int); llvm_options="-print-after-all -filter-print-funcs=julia_,jfptr_")
    output_both = String(take!(io))
    dumps_both = count("IR Dump After", output_both)
    # Matching both julia_ and jfptr_ prefixes should produce more dumps
    @test dumps_both > dumps_one > 0
end

# isbits, isbitstype

@test !isbitstype(Array{Int})
@test isbitstype(Float32)
@test isbitstype(Int)
@test !isbitstype(AbstractString)
@test isbitstype(Tuple{Int, Vararg{Int, 2}})
@test !isbitstype(Tuple{Int, Vararg{Int}})
@test !isbitstype(Tuple{Integer, Vararg{Int, 2}})
@test isbitstype(Tuple{Int, Vararg{Any, 0}})
@test isbitstype(Tuple{Vararg{Any, 0}})
@test isbits(1)
@test isbits((1,2))
@test !isbits([1])
@test isbits(nothing)
@test fully_eliminated(isbits, (Int,))

# issue #16670
@test isconcretetype(Int)
@test isconcretetype(Vector{Int})
@test isconcretetype(Tuple{Int, Vararg{Int, 2}})
@test !isconcretetype(Tuple{Any})
@test !isconcretetype(Tuple{Integer, Vararg{Int, 2}})
@test !isconcretetype(Tuple{Int, Vararg{Int}})
@test !isconcretetype(Type{Tuple{Integer, Vararg{Int}}})
@test !isconcretetype(Type{Vector})
@test !isconcretetype(Type{Int})
@test !isconcretetype(Tuple{Type{Int}})
@test isconcretetype(DataType)
@test isconcretetype(Union)
@test !isconcretetype(Union{})
@test !isconcretetype(Complex)
@test !isconcretetype(Complex.body)
@test !isconcretetype(AbstractArray{Int,1})
struct AlwaysHasLayout{T}
    x
end
@test !isconcretetype(AlwaysHasLayout) && !isconcretetype(AlwaysHasLayout.body)
@test isconcretetype(AlwaysHasLayout{Any})
@test isconcretetype(Ptr{Cvoid})
@test !isconcretetype(Ptr) && !isconcretetype(Ptr.body)

# issue #10165
i10165(::Type) = 0
i10165(::Type{AbstractArray{T,n}}) where {T,n} = 1
@test i10165(AbstractArray{Int,n} where n) == 0
@test which(i10165, Tuple{Type{AbstractArray{Int,n} where n},}).sig == Tuple{typeof(i10165),Type}

# fullname
@test fullname(Base) == (:Base,)
@test fullname(Base.Iterators) == (:Base, :Iterators)

const a_const = 1
not_const = 1
@test isconst(@__MODULE__, :a_const) == true
@test isconst(Base, :pi) == true
@test isconst(@__MODULE__, :pi) == true
@test isconst(GlobalRef(@__MODULE__, :pi)) == true
@test isconst(@__MODULE__, :not_const) == false
@test isconst(@__MODULE__, :is_not_defined) == false

@test ismutable(1) == false
@test ismutable([]) == true
@test ismutabletype(Int) == false
@test ismutabletype(Vector{Any}) == true
@test ismutabletype(Union{Int, Vector{Any}}) == false

## find bindings tests
@test ccall(:jl_get_module_of_binding, Any, (Any, Any), Base, :sin)==Base

# For curmod_*
include("testenv.jl")

module TestMod36529
    x36529 = 0
    y36529 = 1
    export y36529
end

module TestMod7648
using Test
import Base.convert
import ..curmod_name, ..curmod
using ..TestMod36529: x36529   # doesn't import TestMod36529 or y36529, even though it's exported
export a9475, c7648, f9475, foo7648, foo7648_nomethods, Foo7648

const c7648 = 8
d7648 = 9
const f7648 = 10
foo7648(x) = x
function foo7648_nomethods end
mutable struct Foo7648 end

module TestModSub9475
    using Test
    using ..TestMod7648
    import ..curmod_name
    export a9475, f9475, f54609
    a9475 = 5
    b9475 = 7
    f9475(x) = x
    f54609(x) = x
    let
        @test Base.binding_module(@__MODULE__, :a9475) == @__MODULE__
        @test Base.binding_module(@__MODULE__, :c7648) == TestMod7648
        @test Base.nameof(@__MODULE__) === :TestModSub9475
        @test Base.fullname(@__MODULE__) == (curmod_name..., :TestMod7648, :TestModSub9475)
        @test Base.parentmodule(@__MODULE__) == TestMod7648
    end
end # module TestModSub9475

using .TestModSub9475

let
    @test Base.binding_module(@__MODULE__, :d7648) == @__MODULE__
    @test Base.binding_module(@__MODULE__, :a9475) == TestModSub9475
    @test Base.nameof(@__MODULE__) === :TestMod7648
    @test Base.parentmodule(@__MODULE__) == curmod
end
end # module TestMod7648

let
    @test Base.binding_module(TestMod7648, :d7648) == TestMod7648
    @test Base.binding_module(TestMod7648, :a9475) == TestMod7648.TestModSub9475
    @test Base.binding_module(TestMod7648.TestModSub9475, :b9475) == TestMod7648.TestModSub9475
    defaultset = Set(Symbol[:Foo7648, :TestMod7648, :a9475, :c7648, :f9475, :foo7648, :foo7648_nomethods])
    allset = defaultset ∪ Set(Symbol[
        Symbol("#foo7648"), Symbol("#foo7648_nomethods"), Symbol("#_internal_julia_parse"),
        :TestModSub9475, :d7648, :eval, :f7648, :include])
    imported = Set(Symbol[:convert, :curmod_name, :curmod])
    usings_from_Test = Set(Symbol[
        Symbol("@inferred"), Symbol("@test"), Symbol("@test_broken"), Symbol("@test_deprecated"),
        Symbol("@test_logs"), Symbol("@test_nowarn"), Symbol("@test_skip"), Symbol("@test_throws"),
        Symbol("@test_warn"), Symbol("@testset"), :GenericArray, :GenericDict, :GenericOrder,
        :GenericSet, :GenericString, :LogRecord, :Test, :TestLogger, :TestSetException,
        :detect_ambiguities, :detect_closure_boxes, :detect_closure_boxes_all_modules, :detect_unbound_args])
    usings_from_Base = delete!(Set(names(Module(); usings=true)), :anonymous) # the name of the anonymous module itself
    usings = Set(Symbol[:x36529, :TestModSub9475, :f54609]) ∪ usings_from_Test ∪ usings_from_Base
    @test Set(names(TestMod7648)) == defaultset
    @test Set(names(TestMod7648, all=true)) == allset
    @test Set(names(TestMod7648, all=true, imported=true)) == allset ∪ imported
    @test Set(names(TestMod7648, usings=true)) == defaultset ∪ usings
    @test Set(names(TestMod7648, all=true, usings=true)) == allset ∪ usings
    @test isconst(TestMod7648, :c7648)
    @test !isconst(TestMod7648, :d7648)
end

# tests for `names(...; usings=true)`

baremodule Test54609Simple
module Inner
export exported
global exported::Int = 1
global unexported::Int = 0
end
using Base: @assume_effects
using .Inner
end
let usings = names(Test54609Simple; usings=true)
    @test Symbol("@assume_effects") ∈ usings
    @test :Base ∉ usings
    @test :exported ∈ usings
    @test :unexported ∉ usings
end # baremodule Test54609Simple

baremodule _Test54609Complex
export exported_new
using Base: @deprecate_binding
global exported_new = nothing
@deprecate_binding exported_old exported_new
end # baremodule _Test54609Complex
baremodule Test54609Complex
using .._Test54609Complex
end # baremodule Test54609Complex
let usings = names(Test54609Complex; usings=true)
    @test :exported_new ∈ usings
    @test :exported_old ∉ usings
    @test :_Test54609Complex ∈ usings # should include the `using`ed module itself
    usings_all = names(Test54609Complex; usings=true, all=true)
    @test :exported_new ∈ usings_all
    @test :exported_old ∈ usings_all # deprecated names should be included with `all=true`
end

module TestMod54609
module M1
    const m1_x = 1
    export m1_x
end
module M2
    const m2_x = 1
    export m2_x
end
module A
    module B
        f(x) = 1
        secret = 1
        module Inner2 end
    end
    module C
        x = 1
        y = 2
        export y
    end
    using .B: f
    using .C
    using ..M1
    import ..M2
end
end # module TestMod54609
let defaultset = Set((:A,))
    imported = Set((:M2,))
    usings_from_Base = delete!(Set(names(Module(); usings=true)), :anonymous) # the name of the anonymous module itself
    usings = Set((:A, :f, :C, :y, :M1, :m1_x)) ∪ usings_from_Base
    allset = Set((:A, :B, :C, :eval, :include, Symbol("#_internal_julia_parse")))
    @test Set(names(TestMod54609.A)) == defaultset
    @test Set(names(TestMod54609.A, imported=true)) == defaultset ∪ imported
    @test Set(names(TestMod54609.A, usings=true)) == defaultset ∪ usings
    @test Set(names(TestMod54609.A, all=true)) == allset
    @test Set(names(TestMod54609.A, all=true, usings=true)) == allset ∪ usings
    @test Set(names(TestMod54609.A, imported=true, usings=true)) == defaultset ∪ imported ∪ usings
    @test Set(names(TestMod54609.A, all=true, imported=true, usings=true)) == allset ∪ imported ∪ usings
end

module _TestModNamesNoMaterialize
    export foo, bar, baz
    foo() = 1
    bar() = 2
    baz() = 3
end
module _TestModNamesNoMaterializeUser
    using .._TestModNamesNoMaterialize
end
@testset "names does not materialize binding partitions" begin
    # names(m) should not materialize binding partitions for implicit imports,
    # as doing so can trigger unnecessary invalidation cascades during
    # subsequent `using` statements.
    B = _TestModNamesNoMaterializeUser
    # Create bindings in B for the using'd exports without materializing partitions
    for sym in (:foo, :bar, :baz)
        ccall(:jl_get_module_binding, Ref{Core.Binding}, (Any, Any, Cint), B, sym, Cint(1))
    end
    names(B)
    # Verify partitions were not materialized as a side effect
    for sym in (:foo, :bar, :baz)
        b = ccall(:jl_get_module_binding_or_nothing, Any, (Any, Any), B, sym)
        @test b !== nothing
        @test !isdefined(b, :partitions)
    end
end

@testset "names world argument" begin
    # `names(M; all=true)` walks bindings and filters by partition kind at the
    # given world. A binding that doesn't yet have a GLOBAL/CONST/DECLARED
    # partition in the queried world should be excluded.
    # Note: this can only be tested via plain assignment (not `export`/`public`,
    # which set a non-partitioned per-binding flag that is world-insensitive).
    M = @eval module $(gensym())
        early_name = 1
    end
    world_mid = Base.get_world_counter()
    @eval M late_name = 2
    world_end = Base.get_world_counter()

    @test :early_name ∈ names(M; all=true, world=world_end)
    @test :late_name ∈ names(M; all=true, world=world_end)

    @test :early_name ∈ names(M; all=true, world=world_mid)
    @test :late_name ∉ names(M; all=true, world=world_mid)
end

let
    using .TestMod7648
    @test Base.binding_module(@__MODULE__, :a9475) == TestMod7648.TestModSub9475
    @test Base.binding_module(@__MODULE__, :c7648) == TestMod7648
    @test nameof(foo7648) === :foo7648
    @test parentmodule(foo7648, (Any,)) == TestMod7648
    @test parentmodule(foo7648) == TestMod7648
    @test parentmodule(foo7648_nomethods) == TestMod7648
    @test parentmodule(f9475, (Any,)) == TestMod7648.TestModSub9475
    @test parentmodule(f9475) == TestMod7648.TestModSub9475
    @test parentmodule(Foo7648) == TestMod7648
    @test parentmodule(first(methods(f9475))) == TestMod7648.TestModSub9475
    @test parentmodule(first(methods(foo7648))) == TestMod7648
    @test nameof(Foo7648) === :Foo7648
    @test basename(functionloc(foo7648, (Any,))[1]) == "reflection.jl"
    @test first(methods(TestMod7648.TestModSub9475.foo7648)) == which(foo7648, (Int,))
    @test TestMod7648 == which(@__MODULE__, :foo7648)
    @test TestMod7648.TestModSub9475 == which(@__MODULE__, :a9475)
end

@test which(===, Tuple{Int, Int}) isa Method
@test length(code_typed(===, Tuple{Int, Int})) === 1
@test only(Base.return_types(===, Tuple{Int, Int})) === Bool

module TestingExported
using Test
include("testenv.jl") # for curmod_str
import Base.isexported
global this_is_not_defined
export this_is_not_defined
public this_is_public
@test_throws ErrorException("\"this_is_not_defined\" is not defined in module Main") which(Main, :this_is_not_defined)
@test_throws ErrorException("\"this_is_not_exported\" is not defined in module Main") which(Main, :this_is_not_exported)
@test isexported(@__MODULE__, :this_is_not_defined)
@test !isexported(@__MODULE__, :this_is_not_exported)
@test !isexported(@__MODULE__, :this_is_public)
const a_value = 1
@test which(@__MODULE__, :a_value) === @__MODULE__
@test_throws ErrorException("\"a_value\" is not defined in module Main") which(Main, :a_value)
@test which(Main, :Core) === Main
@test !isexported(@__MODULE__, :a_value)
@test !Base.ispublic(@__MODULE__, :a_value)
@test Base.ispublic(@__MODULE__, :this_is_not_defined)
@test Base.ispublic(@__MODULE__, :this_is_public)
@test !Base.ispublic(@__MODULE__, :this_is_not_exported)
end

# set_binding_visibility!
module TestBindingVisibility
using Test
public a_public_name
a_public_name() = 1
export an_exported_name
an_exported_name() = 2
no_decl() = 3

@test Base.isexported(@__MODULE__, :an_exported_name)
@test Base.ispublic(@__MODULE__, :a_public_name) && !Base.isexported(@__MODULE__, :a_public_name)
@test !Base.ispublic(@__MODULE__, :no_decl)

# public <-> none round trip
Base.set_binding_visibility!(@__MODULE__, :a_public_name, :none)
@test !Base.ispublic(@__MODULE__, :a_public_name)
Base.set_binding_visibility!(@__MODULE__, :a_public_name, :public)
@test Base.ispublic(@__MODULE__, :a_public_name)

# an exported name is reported public; lowering it to :public clears only the export
@test Base.ispublic(@__MODULE__, :an_exported_name)
Base.set_binding_visibility!(@__MODULE__, :an_exported_name, :public)
@test !Base.isexported(@__MODULE__, :an_exported_name)
@test Base.ispublic(@__MODULE__, :an_exported_name)

# promote an undeclared name to export
Base.set_binding_visibility!(@__MODULE__, :no_decl, :export)
@test Base.isexported(@__MODULE__, :no_decl)

@test_throws ArgumentError Base.set_binding_visibility!(@__MODULE__, :no_decl, :bogus)
end

# PR 13825
let ex = :(a + b)
    @test string(ex) == "a + b"
end
foo13825(::Array{T, N}, ::Array, ::Vector) where {T, N} = nothing
@test startswith(string(first(methods(foo13825))),
                 "foo13825(::Array{T, N}, ::Array, ::Vector) where {T, N}")

mutable struct TLayout
    x::Int8
    y::Int16
    z::Int32
end
tlayout = TLayout(5,7,11)
@test fieldnames(TLayout) == (:x, :y, :z) == Base.propertynames(tlayout)
@test hasfield(TLayout, :y)
@test !hasfield(TLayout, :a)
@test hasfield(Complex, :re)
@test !hasfield(Complex, :qxq)
@test hasproperty(tlayout, :x)
@test !hasproperty(tlayout, :p)
@test [(fieldoffset(TLayout,i), fieldname(TLayout,i), fieldtype(TLayout,i)) for i = 1:fieldcount(TLayout)] ==
    [(0, :x, Int8), (2, :y, Int16), (4, :z, Int32)]
@test [fieldoffset(TLayout, s) for s = (:x, :y, :z)] == [0, 2, 4]
@test fieldnames(Complex) === (:re, :im)
@test_throws BoundsError fieldtype(TLayout, 0)
@test_throws ArgumentError fieldname(TLayout, 0)
@test_throws BoundsError fieldoffset(TLayout, 0)
@test_throws BoundsError fieldtype(TLayout, 4)
@test_throws ArgumentError fieldname(TLayout, 4)
@test_throws BoundsError fieldoffset(TLayout, 4)

@test fieldtype(Tuple{Vararg{Int8}}, 1) === Int8
@test fieldtype(Tuple{Vararg{Int8}}, 10) === Int8
@test_throws BoundsError fieldtype(Tuple{Vararg{Int8}}, 0)
# issue #30505
@test fieldtype(Union{Tuple{Char},Tuple{Char,Char}},2) === Char
@test_throws BoundsError fieldtype(Union{Tuple{Char},Tuple{Char,Char}},3)

@test [fieldindex(TLayout, i) for i = (:x, :y, :z)] == [1, 2, 3]
@test fieldname(TLayout, fieldindex(TLayout, :z)) === :z
@test fieldindex(TLayout, fieldname(TLayout, 3)) === 3

@test fieldnames(NTuple{3, Int}) == ntuple(i -> fieldname(NTuple{3, Int}, i), 3) == (1, 2, 3)
@test_throws ArgumentError fieldnames(Union{})
@test_throws BoundsError fieldname(NTuple{3, Int}, 0)
@test_throws BoundsError fieldname(NTuple{3, Int}, 4)

@test fieldnames(NamedTuple{(:z,:a)}) === (:z,:a)
@test fieldname(NamedTuple{(:z,:a)}, 1) === :z
@test fieldname(NamedTuple{(:z,:a)}, 2) === :a
@test_throws ArgumentError fieldname(NamedTuple{(:z,:a)}, 3)
@test_throws ArgumentError fieldnames(NamedTuple)
@test_throws ArgumentError fieldnames(NamedTuple{T,Tuple{Int,Int}} where T)
@test_throws ArgumentError fieldnames(Real)
@test_throws ArgumentError fieldnames(AbstractArray)

# Common-field unions keep field-index queries structural without choosing a representative arm.
@test fieldindex(Union{Base.RefValue{Int},Base.RefValue{Float64}}, :x) == 1
@test hasfield(Union{Base.RefValue{Int},Base.RefValue{Float64}}, :x)
@test Core.Compiler.try_compute_fieldidx(Union{Base.RefValue{Int},Base.RefValue{Float64}}, :x) == 1
@test Core.Compiler.try_compute_fieldidx(Type{Base.RefValue{Int}}, :x) === nothing
@test fieldindex(Union{Ref{Int},Ref{Float64}}, :x, false) == 0
@test !hasfield(Union{Ref{Int},Ref{Float64}}, :x)
@test_throws FieldError fieldindex(Union{Ref{Int},Ref{Float64}}, :x)

@test fieldtype((NamedTuple{T,Tuple{Int,String}} where T), 1) === Int
@test fieldtype((NamedTuple{T,Tuple{Int,String}} where T), 2) === String
@test_throws BoundsError fieldtype((NamedTuple{T,Tuple{Int,String}} where T), 3)

@test fieldtype(NamedTuple, 42) === Any
@test_throws BoundsError fieldtype(NamedTuple, 0)
@test_throws BoundsError fieldtype(NamedTuple, -1)

@test fieldtype(NamedTuple{(:a,:b)}, 1) === Any
@test fieldtype(NamedTuple{(:a,:b)}, 2) === Any
@test fieldtype((NamedTuple{(:a,:b),T} where T<:Tuple{Vararg{Integer}}), 2) === Integer
@test_throws BoundsError fieldtype(NamedTuple{(:a,:b)}, 3)

# issue #32697
@test fieldtype(NamedTuple{(:x,:y), T} where T <: Tuple{Int, Union{Float64, Missing}}, :x) == Int
@test fieldtype(NamedTuple{(:x,:y), T} where T <: Tuple{Int, Union{Float64, Missing}}, :y) == Union{Float64, Missing}

@test fieldtypes(NamedTuple{(:a,:b)}) == (Any, Any)
@test fieldtypes((NamedTuple{T,Tuple{Int,String}} where T)) === (Int, String)
@test fieldtypes(TLayout) === (Int8, Int16, Int32)

import Base: datatype_alignment, return_types
@test datatype_alignment(UInt16) == 2
@test datatype_alignment(TLayout) == 4
let rts = return_types(TLayout)
    @test length(rts) == 2 # general constructor and specific constructor
    @test all(rts .== TLayout)
end

# issue #15447
f15447_line = @__LINE__() + 1
@noinline function f15447(s, a)
    if s
        return a
    else
        nb = 0
        return nb
    end
end
@test functionloc(f15447)[2] == f15447_line

# issue #14346
@noinline function f14346(id, mask, limit)
    if id <= limit && mask[id]
        return true
    end
end
@test functionloc(f14346)[2] == @__LINE__() - 5

# issue #15714
# show variable names for slots and suppress spurious type warnings
function f15714(array_var15714)
    for index_var15714 in eachindex(array_var15714)
        array_var15714[index_var15714] += 0
    end
end

function g15714(array_var15714)
    for index_var15714 in eachindex(array_var15714)
        array_var15714[index_var15714] += 0
    end
    let index_var15714
        for outer index_var15714 in eachindex(array_var15714)
            array_var15714[index_var15714] += 0
        end
        index_var15714
    end
    let index_var15714
        for outer index_var15714 in eachindex(array_var15714)
            array_var15714[index_var15714] += 0
        end
        index_var15714
    end
end

import InteractiveUtils.code_warntype

used_dup_var_tested15714 = false
used_unique_var_tested15714 = false
function test_typed_ir_printing(Base.@nospecialize(f), Base.@nospecialize(types), must_used_vars)
    src, rettype = code_typed(f, types, optimize=false)[1]
    dupnames = Set()
    slotnames = Set()
    for name in src.slotnames
        if name in slotnames || name === Symbol("")
            push!(dupnames, name)
        else
            push!(slotnames, name)
        end
    end
    # Make sure must_used_vars are in slotnames
    for name in must_used_vars
        @test name in slotnames
    end
    must_used_checked = Dict{Symbol,Bool}()
    for sym in must_used_vars
        must_used_checked[sym] = false
    end
    for str in (sprint(io -> code_warntype(io, f, types, optimize=false)),
                repr("text/plain", src))
        for var in must_used_vars
            @test occursin(string(var), str)
        end
        # Check that we are not printing the bare slot numbers
        for i in 1:length(src.slotnames)
            name = src.slotnames[i]
            if name in dupnames
                if name in must_used_vars && occursin(Regex("_$i\\b"), str)
                    must_used_checked[name] = true
                    global used_dup_var_tested15714 = true
                end
            else
                @test !occursin(Regex("_$i\\b"), str)
                if name in must_used_vars
                    global used_unique_var_tested15714 = true
                end
            end
        end
    end
    for sym in must_used_vars
        if sym in dupnames
            @test must_used_checked[sym]
        end
        must_used_checked[sym] = false
    end
    # Make sure printing an AST outside CodeInfo still works.
    str = sprint(show, src.code)
    # Check that we are printing the slot numbers when we don't have the context
    # Use the variable names that we know should be present in the optimized AST
    for i in 2:length(src.slotnames)
        name = src.slotnames[i]
        if name in must_used_vars && occursin(Regex("_$i\\b"), str)
            must_used_checked[name] = true
        end
    end
    for sym in must_used_vars
        @test must_used_checked[sym]
    end
end
test_typed_ir_printing(f15714, Tuple{Vector{Float32}},
                       [:array_var15714,  :index_var15714])
test_typed_ir_printing(g15714, Tuple{Vector{Float32}},
                       [:array_var15714,  :index_var15714])
#This test doesn't work with the new optimizer because we drop slotnames
#We may want to test it against debug info eventually
#@test used_dup_var_tested15715
@test used_unique_var_tested15714

let li = only(methods(fieldtype)).unspecialized,
    lrepr = string(li),
    mrepr = string(li.def),
    lmime = repr("text/plain", li),
    mmime = repr("text/plain", li.def)

    @test lrepr == lmime == "MethodInstance for fieldtype(::Vararg{Any})"
    @test mrepr == "fieldtype(...) @ Core none:0"       # simple print
    @test mmime == "fieldtype(...)\n     @ Core none:0" # verbose print
end

# Linfo Tracing test
function tracefoo end
# Method Tracing test
methtracer(x::Ptr{Cvoid}) = (@test isa(unsafe_pointer_to_objref(x), Method); global didtrace = true; nothing)
let cmethtracer = @cfunction(methtracer, Cvoid, (Ptr{Cvoid},))
    ccall(:jl_register_newmeth_tracer, Cvoid, (Ptr{Cvoid},), cmethtracer)
end
didtrace = false
tracefoo2(x, y) = x*y
@test didtrace
didtrace = false
tracefoo(x::Int64, y::Int64) = x*y
@test didtrace
didtrace = false
ccall(:jl_register_newmeth_tracer, Cvoid, (Ptr{Cvoid},), C_NULL)

# test for reflection over large method tables
for i = 1:100; @eval fLargeTable(::Val{$i}, ::Any) = 1; end
for i = 1:100; @eval fLargeTable(::Any, ::Val{$i}) = 2; end
fLargeTable(::Any...) = 3
@test length(methods(fLargeTable, Tuple{})) == 1
fLargeTable(::Complex, ::Complex) = 4
fLargeTable(::Union{ComplexF32, ComplexF64}...) = 5
@test length(methods(fLargeTable, Tuple{})) == 1
fLargeTable() = 4
@test length(methods(fLargeTable)) == 204
@test length(methods(fLargeTable, Tuple{})) == 1
@test fLargeTable(1im, 2im) == 4
@test fLargeTable(1.0im, 2.0im) == 5
@test_throws MethodError fLargeTable(Val(1), Val(1))
@test fLargeTable(Val(1), 1) == 1
@test fLargeTable(1, Val(1)) == 2
fLargeTable(::Union, ::Union) = "a"
@test fLargeTable(Union{Int, Missing}, Union{Int, Missing}) == "a"
fLargeTable(::Union, ::Union) = "b"
@test length(methods(fLargeTable)) == 205
@test fLargeTable(Union{Int, Missing}, Union{Int, Missing}) == "b"

# issue #58479 (updated for #33136: a type value `v` matches `::Type{<:DataType}`
# only if `v <: DataType`, and a `Type{X}` is no longer a subtype of any single
# kind -- except wrapper-like classes like `Vector`, whose whole cover is `UnionAll`)
fLargeTable(::Type) = "Type"
fLargeTable(::Type{<:DataType}) = "DataType"
@test fLargeTable(Type) == "Type"
@test fLargeTable(DataType) == "DataType"
@test fLargeTable(Type{DataType}) == "Type"
@test fLargeTable(Type{UnionAll}) == "Type"
@test fLargeTable(Type{Int}) == "Type"
@test fLargeTable(Type{Vector}) == "Type"
@test fLargeTable(Type{Type{Union{}}}) == "Type"
@test fLargeTable(Type{Union{}}) == "Type"
@test fLargeTable(Union{}) == "DataType"
@test fLargeTable(Type{<:DataType}) == "Type"
fLargeTable(::Type{<:UnionAll}) = "UnionAll"
@test fLargeTable(UnionAll) == "UnionAll"
@test fLargeTable(Type{Vector}) == "UnionAll"
@test fLargeTable(Type{Int}) == "Type"
@test fLargeTable(Type{Type{Union{}}}) == "Type"
@test fLargeTable(Type{Union{}}) == "Type"
@test_throws MethodError fLargeTable(Union{})
@test fLargeTable(Type{<:DataType}) == "Type"
@test fLargeTable(Type{Vector{T}} where T) == "Type"
@test fLargeTable(Union{DataType,Type{Vector{T}} where T}) == "Type"
@test fLargeTable(Union{DataType,UnionAll,Type{Vector{T}} where T}) == "Type"
@test fLargeTable(Union{Type{Vector},Type{Vector{T}} where T}) == "Type"

# issue #15280
function f15280(x) end
@test functionloc(f15280)[2] > 0

# bug found in #16850, Base.url with backslashes on Windows
function module_depth(from::Module, to::Module)
    if from === to || parentmodule(to) === to
        return 0
    else
        return 1 + module_depth(from, parentmodule(to))
    end
end
function has_backslashes(mod::Module)
    for n in names(mod, all = true, imported = true)
        isdefinedglobal(mod, n) || continue
        Base.isdeprecated(mod, n) && continue
        f = getglobal(mod, n)
        if isa(f, Module) && module_depth(Main, f) <= module_depth(Main, mod)
            continue
        end
        h = has_backslashes(f)
        h === nothing || return h
    end
    return nothing
end
function has_backslashes(f::Function)
    for m in methods(f)
        h = has_backslashes(m)
        h === nothing || return h
    end
    return nothing
end
function has_backslashes(meth::Method)
    if '\\' in string(meth.file)
        return meth
    else
        return nothing
    end
end
has_backslashes(x) = nothing
h16850 = has_backslashes(Base)
if Sys.iswindows()
    if h16850 === nothing
        @warn """No methods found in Base with backslashes in file name,
                 skipping test for `Base.url`"""
    else
        @test !('\\' in Base.url(h16850))
    end
else
    @test h16850 === nothing
end

# PR #18888: code_typed shouldn't cache, return_types should
f18888() = nothing
let
    world = Core.Compiler.get_world_counter()
    m = first(methods(f18888, Tuple{}))
    ft = typeof(f18888)

    code_typed(f18888, Tuple{}; optimize=false)
    @test m.specializations !== Core.svec() # uncached, but creates the specializations entry
    mi = Core.Compiler.specialize_method(m, Tuple{ft}, Core.svec())
    interp = Core.Compiler.NativeInterpreter(world)
    @test !Core.Compiler.haskey(Core.Compiler.code_cache(interp), mi)
    @test !isdefined(mi, :cache)

    code_typed(f18888, Tuple{}; optimize=true)
    @test !isdefined(mi, :cache)

    Base.return_types(f18888, Tuple{})
    @test Core.Compiler.getindex(Core.Compiler.code_cache(interp), mi) === mi.cache
    @test mi.cache isa Core.CodeInstance
    @test !isdefined(mi.cache, :next)
end

# code_typed_by_type
@test any(code -> code[2] == Val, Base.code_typed_by_type(Tuple{Type{<:Val}}))
@test Base.code_typed_by_type(Tuple{typeof(sin), Float64})[1][2] === Float64

# signature-based code_typed(...)
@test any(code -> code[2] == Val, Base.code_typed((Type{<:Val},)))
@test Base.code_typed((typeof(sin), Float64))[1][2] === Float64

# New reflection methods in 0.6
struct ReflectionExample{T<:AbstractFloat, N}
    x::Tuple{T, N}
end

@test !isabstracttype(Union{})
@test !isabstracttype(Union{Int,Float64})
@test isabstracttype(AbstractArray)
@test isabstracttype(AbstractSet{Int})
@test !isabstracttype(ReflectionExample)
@test !isabstracttype(Int)
@test !isabstracttype(TLayout)
# PR #61915: `Type{T}` (a `TypeEq` kind) is still reported abstract
@test isabstracttype(Type)
@test isabstracttype(Type{<:Integer})

@test !isprimitivetype(Union{})
@test !isprimitivetype(Union{Int,Float64})
@test !isprimitivetype(AbstractArray)
@test !isprimitivetype(AbstractSet{Int})
@test !isprimitivetype(ReflectionExample)
@test isprimitivetype(Int)
@test !isprimitivetype(TLayout)

@test !isstructtype(Union{})
@test !isstructtype(Union{Int,Float64})
@test !isstructtype(AbstractArray)
@test !isstructtype(AbstractSet{Int})
@test isstructtype(ReflectionExample)
@test !isstructtype(Int)
@test isstructtype(TLayout)

let
    wrapperT(T) = Base.typename(T).wrapper
    @test @inferred wrapperT(ReflectionExample{Float64, Int64}) == ReflectionExample
    @test @inferred wrapperT(ReflectionExample{Float64, N} where N) == ReflectionExample
    @test @inferred wrapperT(ReflectionExample{T, Int64} where T) == ReflectionExample
    @test @inferred wrapperT(ReflectionExample) == ReflectionExample
    @test @inferred wrapperT(Union{ReflectionExample{Union{},1},ReflectionExample{Float64,1}}) == ReflectionExample
    @test_throws(Core.TypeNameError(Union{Int, Float64}),
                 Base.typename(Union{Int, Float64}))
end

# sizeof and nfields
@test sizeof(Int16) == 2
@test sizeof(ComplexF64) == 16
primitive type ParameterizedByte__{A,B} 8 end
@test sizeof(ParameterizedByte__) == 1
@test sizeof(nothing) == 0
@test sizeof(()) == 0
struct TypeWithIrrelevantParameter{T}
    x::Int32
end
@test sizeof(TypeWithIrrelevantParameter) == sizeof(Int32)
@test sizeof(TypeWithIrrelevantParameter{Int8}) == sizeof(Int32)
@test sizeof(:abc) == 3
@test sizeof(Symbol("")) == 0
@test_throws(ErrorException("Abstract type Real does not have a definite size."),
             sizeof(Real))
@test sizeof(Union{ComplexF32,ComplexF64}) == 16
@test sizeof(Union{Int8,UInt8}) == 1
@test sizeof(MemoryRef{Int}) == 2 * sizeof(Int)
@test sizeof(GenericMemoryRef{:atomic,Int,Core.CPU}) == 2 * sizeof(Int)
@test sizeof(Array{Int,0}) == 2 * sizeof(Int)
@test sizeof(Array{Int,1}) == 3 * sizeof(Int)
@test sizeof(Array{Int,2}) == 4 * sizeof(Int)
@test sizeof(Array{Int,20}) == 22 * sizeof(Int)
@test_throws ErrorException sizeof(Tuple)
@test_throws ErrorException sizeof(Tuple{Any,Any})
@test_throws ErrorException sizeof(String)
@test_throws ErrorException sizeof(Memory{false,Int})
@test_throws ErrorException sizeof(Symbol)
@test_throws ErrorException sizeof(Core.SimpleVector)
@test_throws ErrorException sizeof(Union{})

@test nfields((1,2)) == 2
@test nfields(()) == 0
@test nfields(nothing) == fieldcount(Nothing) == 0
@test nfields(1) == 0
@test_throws ArgumentError fieldcount(Union{})
@test fieldcount(Tuple{Any,Any,T} where T) == 3
@test fieldcount(Complex) == fieldcount(ComplexF32) == 2
@test fieldcount(Union{ComplexF32,ComplexF64}) == 2
@test fieldcount(Int) == 0
@test_throws(ArgumentError("type does not have a definite number of fields"),
             fieldcount(Union{Complex,Pair}))
@test_throws ArgumentError fieldcount(Real)
@test_throws ArgumentError fieldcount(AbstractArray)
@test_throws ArgumentError fieldcount(Tuple{Any,Vararg{Any}})

# Common-field unions are definite only when all alternatives share the field count.
@test fieldcount(Union{Tuple{Int,Float64},Tuple{Int,Int}}) == 2
@test fieldtypes(Union{Tuple{Int,Float64},Tuple{Int,Int}}) == (Int, Union{Float64,Int})
@test_throws(ArgumentError("type does not have a definite number of fields"),
             fieldcount(Union{Tuple{Int},Tuple{Int,Int}}))
@test_throws(ArgumentError("type does not have a definite number of fields"),
             fieldtypes(Union{Tuple{Int},Tuple{Int,Int}}))

# PR #22979

function test_similar_codeinfo(a, b)
    @test a.code == b.code
    @test a.slotnames == b.slotnames
    @test a.slotflags == b.slotflags
end

@generated f22979(x...) = (y = 1; :(x[1] + x[2]))
let
    x22979 = (1, 2.0, 3.0 + im)
    T22979 = Tuple{typeof(f22979), typeof.(x22979)...}
    world = Core.Compiler.get_world_counter()
    match = Base._methods_by_ftype(T22979, -1, world)[1]
    instance = Core.Compiler.specialize_method(match)
    cinfo_generated = Core.Compiler.get_staged(instance, world)
    @test_throws ErrorException Base.uncompressed_ir(match.method)

    test_similar_codeinfo(code_lowered(f22979, typeof(x22979))[1], cinfo_generated)

    cinfos = code_lowered(f22979, typeof.(x22979), generated=true)
    @test length(cinfos) == 1
    cinfo = cinfos[1]
    test_similar_codeinfo(cinfo, cinfo_generated)
    @test_throws ErrorException code_lowered(f22979, typeof.(x22979), generated=false)
end


module MethodDeletion
using Test, Random

# Deletion after compiling top-level call
bar1(x) = 1
bar1(x::Int) = 2
foo1(x) = bar1(x)
faz1(x) = foo1(x)
@test faz1(1) == 2
@test faz1(1.0) == 1
m = first(methods(bar1, Tuple{Int}))
Base.delete_method(m)
@test bar1(1) == 1
@test bar1(1.0) == 1
@test foo1(1) == 1
@test foo1(1.0) == 1
@test faz1(1) == 1
@test faz1(1.0) == 1

# Deletion after compiling middle-level call
bar2(x) = 1
bar2(x::Int) = 2
foo2(x) = bar2(x)
faz2(x) = foo2(x)
@test foo2(1) == 2
@test foo2(1.0) == 1
m = first(methods(bar2, Tuple{Int}))
Base.delete_method(m)
@test bar2(1.0) == 1
@test bar2(1) == 1
@test foo2(1) == 1
@test foo2(1.0) == 1
@test faz2(1) == 1
@test faz2(1.0) == 1

# Deletion after compiling low-level call
bar3(x) = 1
bar3(x::Int) = 2
foo3(x) = bar3(x)
faz3(x) = foo3(x)
@test bar3(1) == 2
@test bar3(1.0) == 1
m = first(methods(bar3, Tuple{Int}))
Base.delete_method(m)
@test bar3(1) == 1
@test bar3(1.0) == 1
@test foo3(1) == 1
@test foo3(1.0) == 1
@test faz3(1) == 1
@test faz3(1.0) == 1

# Deletion before any compilation
bar4(x) = 1
bar4(x::Int) = 2
foo4(x) = bar4(x)
faz4(x) = foo4(x)
m = first(methods(bar4, Tuple{Int}))
Base.delete_method(m)
@test bar4(1) == 1
@test bar4(1.0) == 1
@test foo4(1) == 1
@test foo4(1.0) == 1
@test faz4(1) == 1
@test faz4(1.0) == 1

# Deletion & invoke (issue #48802)
function f48802!(log, x::Integer)
    log[] = "default"
    return x + 1
end
function addmethod_48802()
    @eval function f48802!(log, x::Int)
        ret = invoke(f48802!, Tuple{Any, Integer}, log, x)
        log[] = "specialized"
        return ret
    end
end
log = Ref{String}()
@test f48802!(log, 1) == 2
@test log[] == "default"
addmethod_48802()
@test f48802!(log, 1) == 2
@test log[] == "specialized"
Base.delete_method(which(f48802!, Tuple{Any, Int}))
@test f48802!(log, 1) == 2
@test log[] == "default"
addmethod_48802()
@test f48802!(log, 1) == 2
@test log[] == "specialized"

# Methods with keyword arguments
fookw(x; direction=:up) = direction
fookw(y::Int) = 2
@test fookw("string") === :up
@test fookw(1) == 2
m = collect(methods(fookw))[2]
Base.delete_method(m)
@test fookw(1) == 2
@test_throws MethodError fookw("string")

# functions with many methods
types = (Float64, Int32, String)
for T1 in types, T2 in types, T3 in types
    @eval foomany(x::$T1, y::$T2, z::$T3) = y
end
@test foomany(Int32(5), "hello", 3.2) == "hello"
m = first(methods(foomany, Tuple{Int32, String, Float64}))
Base.delete_method(m)
@test_throws MethodError foomany(Int32(5), "hello", 3.2)

struct EmptyType end
Base.convert(::Type{EmptyType}, x::Integer) = EmptyType()
m = first(methods(convert, Tuple{Type{EmptyType}, Integer}))
Base.delete_method(m)
@test_throws MethodError convert(EmptyType, 1)

# parametric methods
parametric(A::Array{T,N}, i::Vararg{Int,N}) where {T,N} = N
@test parametric(rand(2,2), 1, 1) == 2
m = first(methods(parametric))
Base.delete_method(m)
@test_throws MethodError parametric(rand(2,2), 1, 1)

# Deletion and ambiguity detection
foo(::Int, ::Int) = 1
foo(::Real, ::Int) = 2
foo(::Int, ::Real) = 3
Base.delete_method(first(methods(foo)))
@test_throws MethodError foo(1, 1)
foo(::Int, ::Int) = 1
foo(1, 1)
Base.delete_method(first(methods(foo)))
@test_throws MethodError foo(1, 1)

# multiple deletions and ambiguities
typeparam(::Type{T}, a::Array{T}) where T<:AbstractFloat = 1
typeparam(::Type{T}, a::Array{T}) where T = 2
for mth in collect(methods(typeparam))
    Base.delete_method(mth)
end
typeparam(::Type{T}, a::AbstractArray{T}) where T<:AbstractFloat = 1
typeparam(::Type{T}, a::AbstractArray{T}) where T = 2
@test typeparam(Float64, rand(2))  == 1
@test typeparam(Int, rand(Int, 2)) == 2

# prior ambiguities (issue #28899)
uambig(::Union{Int,Nothing}) = 1
uambig(::Union{Float64,Nothing}) = 2
@test uambig(1) == 1
@test uambig(1.0) == 2
@test_throws MethodError uambig(nothing)
m = which(uambig, Tuple{Int})
Base.delete_method(m)
@test_throws MethodError uambig(1)
@test uambig(1.0) == 2
@test uambig(nothing) == 2

end

module HasmethodKwargs
using Test
f(x::Int; y=3) = x + y
@test hasmethod(f, Tuple{Int})
@test hasmethod(f, Tuple{Int}, ())
@test hasmethod(f, Tuple{Int}, (:y,))
@test !hasmethod(f, Tuple{Int}, (:x,))
@test !hasmethod(f, Tuple{Int}, (:jeff,))
@test !hasmethod(f, Tuple{Int}, (:y,), world=typemin(UInt))
g(; b, c, a) = a + b + c
h(; kwargs...) = 4
for gh = (g, h)
    @test hasmethod(gh, Tuple{})
    @test hasmethod(gh, Tuple{}, ())
    @test hasmethod(gh, Tuple{}, (:a,))
    @test hasmethod(gh, Tuple{}, (:a, :b))
    @test hasmethod(gh, Tuple{}, (:a, :b, :c))
end
@test !hasmethod(g, Tuple{}, (:a, :b, :c, :d))
@test hasmethod(h, Tuple{}, (:a, :b, :c, :d))
end

# issue #31353
function f31353(f, x::Array{<:Dict})
end
@test  hasmethod(f31353, Tuple{Any, Array{D}} where D<:Dict)
@test !hasmethod(f31353, Tuple{Any, Array{D}} where D<:AbstractDict)

# issue #26267
module M26267
import Test
foo(x) = x
end
@test !(:Test in names(M26267, all=true, imported=false))
@test :Test in names(M26267, all=true, imported=true)
@test :Test in names(M26267, all=false, imported=true)

# issue #20872
f20872(::Val{N}, ::Val{N}) where {N} = true
f20872(::Val, ::Val) = false
@test which(f20872, Tuple{Val{N},Val{N}} where N).sig == Tuple{typeof(f20872), Val{N}, Val{N}} where N
@test which(f20872, Tuple{Val,Val}).sig == Tuple{typeof(f20872), Val, Val}
@test which(f20872, Tuple{Val,Val{N}} where N).sig == Tuple{typeof(f20872), Val, Val}
@test_throws ErrorException which(f20872, Tuple{Any,Val{N}} where N)
@test which(Tuple{typeof(f20872), Val{1}, Val{2}}).sig == Tuple{typeof(f20872), Val, Val}

# @locals
using Base: @locals
let
    local x, y
    global z
    @test isempty(keys(@locals))
    x = 1
    @test @locals() == Dict{Symbol,Any}(:x=>1)
    y = ""
    @test @locals() == Dict{Symbol,Any}(:x=>1,:y=>"")
    for i = 8:8
        @test @locals() == Dict{Symbol,Any}(:x=>1,:y=>"",:i=>8)
    end
    for i = 42:42
        local x
        @test @locals() == Dict{Symbol,Any}(:y=>"",:i=>42)
    end
    @test @locals() == Dict{Symbol,Any}(:x=>1,:y=>"")
    x = (y,)
    @test @locals() == Dict{Symbol,Any}(:x=>("",),:y=>"")
end

function _test_at_locals1(::Any, ::Any)
    x = 1
    @test @locals() == Dict{Symbol,Any}(:x=>1)
end
_test_at_locals1(1,1)
function _test_at_locals2(a::Any, ::Any, c::T) where T
    x = 2
    @test @locals() == Dict{Symbol,Any}(:x=>2,:a=>a,:c=>c,:T=>typeof(c))
end
_test_at_locals2(1,1,"")
_test_at_locals2(1,1,0.5f0)

@testset "issue #31687" begin
    import InteractiveUtils._dump_function
    import InteractiveUtils.ArgInfo

    @noinline f31687_child(i) = f31687_nonexistent(i)
    f31687_parent() = f31687_child(0)
    params = Base.CodegenParams()
    _dump_function(ArgInfo(f31687_parent, Tuple{}),
                   #=native=#false, #=wrapper=#false, #=raw=#true,
                   #=dump_module=#true, #=syntax=#:att, #=optimize=#false, :none,
                   #=binary=#false)
end

@test nameof(Any) === :Any
@test nameof(:) === :Colon
@test nameof(Core.Intrinsics.mul_int) === :mul_int
@test nameof(Core.Intrinsics.cglobal) === :cglobal

module TestMod33403
f(x) = 1
f(x::Int) = 2
g() = 3

module Sub
import ..TestMod33403: f
f(x::Char) = 3
end
end

@testset "methods with module" begin
    using .TestMod33403: f, g
    @test length(methods(f)) == 3
    @test length(methods(f, (Int,))) == 1

    @test length(methods(f, TestMod33403)) == 2
    @test length(methods(f, [TestMod33403])) == 2
    @test length(methods(f, (Int,), TestMod33403)) == 1
    @test length(methods(f, (Int,), [TestMod33403])) == 1

    @test length(methods(f, TestMod33403.Sub)) == 1
    @test length(methods(f, [TestMod33403.Sub])) == 1
    @test length(methods(f, (Char,), TestMod33403.Sub)) == 1
    @test length(methods(f, (Int,), TestMod33403.Sub)) == 0

    @test length(methods(g, ())) == 1
end

module BodyFunctionLookup
f1(x, y; a=1) = error("oops")
f2(f::Function, args...; kwargs...) = f1(args...; kwargs...)
end

@testset "bodyfunction" begin
    m = first(methods(BodyFunctionLookup.f1))
    f = Base.bodyfunction(m)
    @test occursin("f1#", String(nameof(f)))
    m = first(methods(BodyFunctionLookup.f2))
    f = Base.bodyfunction(m)
    @test f !== Core._apply_iterate
    @test f !== Core._apply
    @test occursin("f2#", String(nameof(f)))
end


@testset "code_typed(; world)" begin
    mod = @eval module $(gensym()) end

    @eval mod foo() = 1
    world1 = Base.get_world_counter()
    @test only(code_typed(mod.foo, ())).second == Int
    @test only(code_typed(mod.foo, (); world=world1)).second == Int

    @eval mod foo() = 2.
    world2 = Base.get_world_counter()
    @test only(code_typed(mod.foo, ())).second == Float64
    @test only(code_typed(mod.foo, (); world=world1)).second == Int
    @test only(code_typed(mod.foo, (); world=world2)).second == Float64
end

@testset "default_tt" begin
    m = Module()
    @eval m f1() = return
    @test Base.default_tt(m.f1) == Tuple{}
    @eval m f2(a) = return
    @test Base.default_tt(m.f2) == Tuple{Any}
    @eval m f3(a::Integer) = return
    @test Base.default_tt(m.f3) == Tuple{Integer}
    @eval m f4() = return
    @eval m f4(a) = return
    @test Base.default_tt(m.f4) == Tuple
end

@testset "lookup mi" begin
    @test 1+1 == 2
    mi1 = Base.method_instance(+, (Int, Int))
    @test mi1.def.name == :+
    mi2 = Base.method_instance((typeof(+), Int, Int))
    @test mi2.def.name == :+
    mi3 = @ccall jl_method_lookup(Any[+, 1, 1]::Ptr{Any}, 3::Csize_t, Base.get_world_counter()::Csize_t)::Ref{Core.MethodInstance}
    @test mi1 == mi3
    @test mi2 == mi3
end

Base.@assume_effects :terminates_locally function issue41694(x::Int)
    res = 1
    0 ≤ x < 20 || error("bad fact")
    while x > 1
        res *= x
        x -= 1
    end
    return res
end
maybe_effectful(x::Int) = 42
maybe_effectful(x::Any) = unknown_operation()
function f_no_methods end
ambig_effects_test(a::Int, b) = 1
ambig_effects_test(a, b::Int) = 1
ambig_effects_test(a, b) = 1

@testset "Base.infer_return_type[s]" begin
    # generic function case
    @test only(Base.return_types(issue41694, (Int,))) == Base.infer_return_type(issue41694, (Int,)) == Int
    # case when it's not fully covered
    @test only(Base.return_types(issue41694, (Integer,))) == Base.infer_return_type(issue41694, (Integer,)) == Int
    # MethodError case
    @test isempty(Base.return_types(issue41694, (Float64,)))
    @test Base.infer_return_type(issue41694, (Float64,)) == Union{}
    # builtin case
    @test only(Base.return_types(typeof, (Any,))) == Base.infer_return_type(typeof, (Any,)) == DataType
    @test only(Base.return_types(===, (Any,Any))) == Base.infer_return_type(===, (Any,Any)) == Bool
    @test only(Base.return_types(setfield!, ())) == Base.infer_return_type(setfield!, ()) == Union{}
    @test only(Base.return_types(Core.Intrinsics.mul_int, ())) == Base.infer_return_type(Core.Intrinsics.mul_int, ()) == Union{}
end

@testset "Base.infer_effects" begin
    # generic functions
    @test Base.infer_effects(issue41694, (Int,)) |> Core.Compiler.is_terminates
    @test Base.infer_effects((Int,)) do x
        issue41694(x)
    end |> Core.Compiler.is_terminates
    @test Base.infer_effects(issue41694) |> Core.Compiler.is_terminates # use `default_tt`
    let effects = Base.infer_effects(maybe_effectful, (Any,)) # union split
        @test !Core.Compiler.is_consistent(effects)
        @test !Core.Compiler.is_effect_free(effects)
        @test !Core.Compiler.is_nothrow(effects)
        @test !Core.Compiler.is_terminates(effects)
        @test !Core.Compiler.is_nonoverlayed(effects)
    end
    # should account for MethodError
    @test Base.infer_effects(issue41694, (Float64,)) |> !Core.Compiler.is_nothrow # definitive dispatch error
    @test Base.infer_effects(issue41694, (Integer,)) |> !Core.Compiler.is_nothrow # possible dispatch error
    @test Base.infer_effects(f_no_methods) |> !Core.Compiler.is_nothrow # no possible matching methods
    @test Base.infer_effects(ambig_effects_test, (Int,Int)) |> !Core.Compiler.is_nothrow # ambiguity error
    @test Base.infer_effects(ambig_effects_test, (Int,Any)) |> !Core.Compiler.is_nothrow # ambiguity error
    # builtins
    @test Base.infer_effects(typeof, (Any,)) |> Core.Compiler.is_foldable_nothrow
    @test Base.infer_effects(===, (Any,Any)) |> Core.Compiler.is_foldable_nothrow
    @test (Base.infer_effects(setfield!, ()); true) # `builtin_effects` shouldn't throw on empty `argtypes`
    @test (Base.infer_effects(Core.Intrinsics.mul_int, ()); true) # `intrinsic_effects` shouldn't throw on empty `argtypes`
end

@testset "Base.infer_exception_type[s]" begin
    # generic functions
    @test Base.infer_exception_type(issue41694, (Int,)) == only(Base.infer_exception_types(issue41694, (Int,))) == ErrorException
    @test Base.infer_exception_type((Int,)) do x
        issue41694(x)
    end == Base.infer_exception_types((Int,)) do x
        issue41694(x)
    end |> only == ErrorException
    @test Base.infer_exception_type(issue41694) == only(Base.infer_exception_types(issue41694)) == ErrorException # use `default_tt`
    let excts = Base.infer_exception_types(maybe_effectful, (Any,))
        @test any(==(Any), excts)
        @test any(==(Union{}), excts)
    end
    @test Base.infer_exception_type(maybe_effectful, (Any,)) == Any
    # `infer_exception_type` should account for MethodError
    @test Base.infer_exception_type(issue41694, (Float64,)) == MethodError # definitive dispatch error
    @test Base.infer_exception_type(issue41694, (Integer,)) == Union{MethodError,ErrorException} # possible dispatch error
    @test Base.infer_exception_type(f_no_methods) == MethodError # no possible matching methods
    @test Base.infer_exception_type(ambig_effects_test, (Int,Int)) == MethodError # ambiguity error
    @test Base.infer_exception_type(ambig_effects_test, (Int,Any)) == MethodError # ambiguity error
    # builtins
    @test Base.infer_exception_type(typeof, (Any,)) === only(Base.infer_exception_types(typeof, (Any,))) === Union{}
    @test Base.infer_exception_type(===, (Any,Any)) === only(Base.infer_exception_types(===, (Any,Any))) === Union{}
    @test (Base.infer_exception_type(setfield!, ()); Base.infer_exception_types(setfield!, ()); true) # `infer_exception_type[s]` shouldn't throw on empty `argtypes`
    @test (Base.infer_exception_type(Core.Intrinsics.mul_int, ()); Base.infer_exception_types(Core.Intrinsics.mul_int, ()); true) # `infer_exception_type[s]` shouldn't throw on empty `argtypes`
end

@test Base._methods_by_ftype(Tuple{}, -1, Base.get_world_counter()) == Any[]
@test length(methods(Base.Broadcast.broadcasted, Tuple{Any, Any, Vararg})) >
      length(methods(Base.Broadcast.broadcasted, Tuple{Base.Broadcast.BroadcastStyle, Any, Vararg})) >=
      length(methods(Base.Broadcast.broadcasted, Tuple{Base.Broadcast.DefaultArrayStyle{1}, Any, Vararg})) >=
      10

@testset "specializations" begin
    f(x) = 1
    f(1)
    f("hello")
    @test length(Base.specializations(only(methods(f)))) == 2
end

# https://github.com/JuliaLang/julia/issues/48856
@test !Base.ismutationfree(Vector{Any})
@test !Base.ismutationfree(Vector{Symbol})
@test !Base.ismutationfree(Vector{UInt8})
@test !Base.ismutationfree(Vector{Int32})
@test !Base.ismutationfree(Vector{UInt64})

@test Base.ismutationfree(Type{Union{}})

@test !Base.ismutationfree(Core.SimpleVector)

module TestNames

public publicized
export exported

publicized() = 1
exported() = 1
private() = 1

end

@test names(TestNames) == [:TestNames, :exported, :publicized]

# reflections for generated function with abstract input types

# :generated_only function should return failed results if given abstract input types
@generated function generated_only_simple(x)
    if x <: Integer
        return :(x ^ 2)
    else
        return :(x)
    end
end
@test only(Base.return_types(generated_only_simple, (Real,))) ==
      Base.infer_return_type(generated_only_simple, (Real,)) ==
      Core.Compiler.return_type(generated_only_simple, Tuple{Real}) == Any
let (src, rt) = only(code_typed(generated_only_simple, (Real,)))
    @test src isa Method
    @test rt == Any
end

# optionally generated function should return fallback results if given abstract input types
function sub2ind_gen_impl(dims::Type{NTuple{N,Int}}, I...) where N
    ex = :(I[$N] - 1)
    for i = (N - 1):-1:1
        ex = :(I[$i] - 1 + dims[$i] * $ex)
    end
    return :($ex + 1)
end;
function sub2ind_gen_fallback(dims::NTuple{N,Int}, I) where N
    ind = I[N] - 1
    for i = (N - 1):-1:1
        ind = I[i] - 1 + dims[i]*ind
    end
    return ind + 1
end;
function sub2ind_gen(dims::NTuple{N,Int}, I::Integer...) where N
    length(I) == N || error("partial indexing is unsupported")
    if @generated
        return sub2ind_gen_impl(dims, I...)
    else
        return sub2ind_gen_fallback(dims, I)
    end
end;
@test only(Base.return_types(sub2ind_gen, (NTuple,Int,Int,))) == Int
let (src, rt) = only(code_typed(sub2ind_gen, (NTuple,Int,Int,); optimize=false))
    @test src isa CodeInfo
    @test rt == Int
    @test any(iscall((src,sub2ind_gen_fallback)), src.code)
    @test any(iscall((src,error)), src.code)
end

# marking a symbol as public should not "unexport" it
# https://github.com/JuliaLang/julia/issues/52812
module Mod52812
using Test
export a, b
@test_throws ErrorException eval(Expr(:public, :a))
public c
@test_throws ErrorException eval(Expr(:export, :c))
export b
public c
end

@test Base.isexported(Mod52812, :a)
@test Base.isexported(Mod52812, :b)
@test Base.ispublic(Mod52812, :a)
@test Base.ispublic(Mod52812, :b)
@test Base.ispublic(Mod52812, :c) && !Base.isexported(Mod52812, :c)

@test Base.infer_return_type(code_lowered, (Any,)) == Vector{Core.CodeInfo}
@test Base.infer_return_type(code_lowered, (Any,Any)) == Vector{Core.CodeInfo}

@test methods(Union{}) == Any[m.method for m in Base._methods_by_ftype(Tuple{Core.TypeofBottom, Vararg}, 1, Base.get_world_counter())] # issue #55187

# which should not look through const bindings, even if they have the same value
# as a previous implicit import
module SinConst
const sin = Base.sin
end

@test which(SinConst, :sin) === SinConst

# `which` should error if there is not a unique binding that a constant was imported from
module X1ConstConflict
const xconstconflict = 1
export xconstconflict
end
module X2ConstConflict
const xconstconflict = 1
export xconstconflict
end
using .X1ConstConflict, .X2ConstConflict

@test_throws ErrorException which(@__MODULE__, :xconstconflict)
